package com.aliothservice.catfish.order.data;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;

import com.aliothservice.catfish.AppConfigurator;
import com.aliothservice.catfish.admin.data.PlatformRealtimeInfo;
import com.aliothservice.catfish.cart.data.CartDataManager;
import com.aliothservice.catfish.data.TransportationTemperature;
import com.aliothservice.catfish.device.data.DeviceOrder;
import com.aliothservice.catfish.device.data.HumidityDocument;
import com.aliothservice.catfish.device.data.LocationDocument;
import com.aliothservice.catfish.device.data.TemperatureDocument;
import com.aliothservice.catfish.identity.data.IdentityDataManager;
import com.aliothservice.catfish.identity.data.User;
import com.aliothservice.catfish.message.MessageController;
import com.aliothservice.catfish.message.data.Message;
import com.aliothservice.catfish.order.request.CreateOrderRequest;
import com.aliothservice.catfish.order.request.DeleteOrderRequest;
import com.aliothservice.catfish.order.request.OrderInfoRequest;
import com.aliothservice.catfish.order.request.OrderPaymentRequestRequest;
import com.aliothservice.catfish.order.request.SellerAssignVehicleToVehicleOrderRequest;
import com.aliothservice.catfish.order.request.SellerReviewListingRequest;
import com.aliothservice.catfish.order.request.SubmitAppealRequestRequest;
import com.aliothservice.catfish.order.request.WriteProductOrderReviewRequest;
import com.aliothservice.catfish.order.request.WriteVehicleOrderReviewRequest;
import com.aliothservice.catfish.order.request.WriteWarehouseOrderReviewRequest;
import com.aliothservice.catfish.product.data.Driver;
import com.aliothservice.catfish.product.data.ProductDataManager;
import com.aliothservice.catfish.product.data.ProductService;
import com.aliothservice.catfish.product.data.Vehicle;
import com.aliothservice.catfish.product.data.VehicleService;
import com.aliothservice.catfish.product.data.WarehouseService;
import com.aliothservice.catfish.util.DateUtil;
import com.alipay.config.AlipayConfig;
import com.alipay.util.AlipaySubmit;
import com.mongodb.WriteResult;

import push.AndroidNotification;
import push.PushClient;
import push.android.AndroidUnicast;
import push.ios.IOSUnicast;

public class OrderDataManager {
	
	public Map<String, String> createUserOrder(String phoneNumber, CreateOrderRequest req) {
		// create new user order
		UserOrder userOrder = new UserOrder();
		userOrder.setUserId(phoneNumber);
		if (null != req.getVehicle()) {
			userOrder.setVehicle(req.getVehicle());
		}
		if (null != req.getWarehouse()) {
			userOrder.setWarehouse(req.getWarehouse());
		}
		if (null != req.getProduct()) {
			userOrder.setProduct(req.getProduct());
		}
		mongoTemplate.save(userOrder);
		
		float totalAmount = 0;
		
		// validation variables
		List<Map<String, Object>> effectiveVehicleServiceList = new ArrayList<Map<String, Object>>();
		List<Map<String, Object>> effectiveWarehouseServiceList = new ArrayList<Map<String, Object>>();
		List<Map<String, Object>> effectiveProductServiceList = new ArrayList<Map<String, Object>>();
		
		// create vehicle orders as necessary
		if (null != req.getVehicle()) {
			for (Map<String, Object> reqMap : req.getVehicle()) {
				// update service and spec
				Date loadingDate;
				DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
				try {
					loadingDate = formatter.parse((String)reqMap.get("loadingDate"));
				} catch (ParseException e) {
					logger.warn("createUserOrder - invalid date format");
					return null;
				}
				VehicleService service = productDataManager.orderVehicleServiceBy(phoneNumber, (String)reqMap.get("serviceId"), loadingDate);
				if (null == service) {
					if (logger.isDebugEnabled()) {
						logger.debug("createUserOrder - cannot located vehicle service: " + reqMap.get("serviceId"));
					}
					Map<String, String> resMap = new HashMap<String, String>();
					resMap.put("errorCode", "410");
					resMap.put("errorMessage", "buy things you sell? you idiot!");
					return resMap;
				}
				
				// create orders
				CompleteVehicleOrder order = new CompleteVehicleOrder();
				order.setServiceId((String)reqMap.get("serviceId"));
				order.setBuyerId(phoneNumber);
				order.setSellerId(service.getUserId());
				if (null != service.getDeviceSN()) {
					order.setDeviceSN(service.getDeviceSN());
				}
				order.setBuyerName((String)reqMap.get("buyerName"));
				order.setBuyerPhoneNumber((String)reqMap.get("buyerPhoneNumber"));
				if (null != service.getEnterprise()) {
					order.setSellerName(service.getEnterprise());
				} else {
					order.setSellerName(service.getIndividual());
				}
				order.setSellerPhoneNumber(service.getUserId());
				order.setTemperatureSpec((String)reqMap.get("temperatureSpec"));
				order.setOriginAddress((String)reqMap.get("originAddress"));
				order.setDestinationAddress((String)reqMap.get("destinationAddress"));
				order.setLoadingDate(loadingDate);
				if (null != reqMap.get("buyerNote")) {
					order.setBuyerNote((String)reqMap.get("buyerNote"));
				}
				order.setPrice(service.getPrice());
				order.setLengthSpec(service.getLengthSpec());
				order.setFullLoadWeight(service.getFullLoadWeight());
				order.setFullLoadMass(service.getFullLoadMass());
				order.setManInChargeName(service.getManInChargeName());
				order.setManInChargePhoneNumber(service.getManInChargePhoneNumber());
				order.setRouteOrigin(service.getOriginProvince()+service.getOriginCity());
				order.setRouteDestination(service.getDestinationProvince()+service.getDestinationCity());
				order.setStatus(CompleteVehicleOrder.STATUS_UNPAID);
				order.setCreationDate(LocalDateTime.now());
				order.setLastUpdateTime(LocalDateTime.now());
				order.setOrderNumber(userOrder.getId());
				mongoTemplate.save(order);
				totalAmount += service.getPrice().floatValue();
				effectiveVehicleServiceList.add(order.toMap((String)reqMap.get("buyerPhoneNumber")));
			}
		}
		
		// create warehouse orders as necessary
		if (null != req.getWarehouse()) {
			for (Map<String, Object> reqMap : req.getWarehouse()) {
				// update service and spec
				Date startDate, untilDate;
				DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
				try {
					startDate = formatter.parse((String) reqMap.get("startDate"));
					untilDate = formatter.parse((String)reqMap.get("untilDate"));
				} catch (ParseException e) {
					logger.warn("createUserOrder - invalid date format");
					continue;
				}
				WarehouseService service = productDataManager.orderWarehouseServiceById(phoneNumber, (String)reqMap.get("serviceId"), startDate, untilDate, Integer.valueOf((String)reqMap.get("volume")));
				if (null == service) {
					Map<String, String> resMap = new HashMap<String, String>();
					resMap.put("errorCode", "410");
					resMap.put("errorMessage", "buy things you sell? you idiot!");
					return resMap;
				}
				
				// create order
				WarehouseOrder order = new WarehouseOrder();
				order.setServiceId((String)reqMap.get("serviceId"));
				order.setBuyerId(phoneNumber);
				order.setSellerId(service.getUserId());
				if (null != service.getDeviceSN()) {
					order.setDeviceSN(service.getDeviceSN());
				}
				order.setBuyerName((String)reqMap.get("buyerName"));
				order.setBuyerPhoneNumber((String)reqMap.get("buyerPhoneNumber"));
				if (null != service.getEnterprise()) {
					order.setSellerName(service.getEnterprise());
				} else {
					order.setSellerName(service.getIndividual());
				}
				order.setSellerPhoneNumber(service.getUserId());
				order.setStartDate(startDate);
				order.setUntilDate(untilDate);
				order.setVolume(Integer.valueOf((String)reqMap.get("volume")));
				order.setUnitPrice(service.getPrice());
				if (null != reqMap.get("buyerNote")) {
					order.setBuyerNote((String)reqMap.get("buyerNote"));
				}
				order.setWarehouseAddress(service.getProvince()+service.getCity()+service.getDistrict()+service.getAddress());
				order.setWarehouseName(service.getName());
				order.setStockTotal(service.getStockTotal());
				order.setTemperatureSpec(service.getTemperatureSpec());
				order.setManInChargeName(service.getManInChargeName());
				order.setManInChargePhoneNumber(service.getManInChargePhoneNumber());
				order.setStatus(WarehouseOrder.STATUS_UNPAID);
				order.setLastUpdateTime(LocalDateTime.now());
				order.setCreationDate(LocalDateTime.now());
				order.setWarehousePhotoId(service.getWarehousePhotoId());
				order.setOrderNumber(userOrder.getId());
				mongoTemplate.save(order);
				int days = DateUtil.daysBetween(startDate, untilDate);
				totalAmount += (order.getUnitPrice().floatValue()*order.getVolume().intValue()*days);
				effectiveWarehouseServiceList.add(order.toMap((String)reqMap.get("buyerPhoneNumber")));
			}
		}
		
		// create product orders as necessary
		if (null != req.getProduct()) {
			for (Map<String, Object> reqMap : req.getProduct()) {
				// update service and spec
				ProductService service = productDataManager.orderProductServiceById(phoneNumber, (String)reqMap.get("serviceId"), Integer.valueOf((String)reqMap.get("volume")));
				if (null == service) {
					Map<String, String> resMap = new HashMap<String, String>();
					resMap.put("errorCode", "410");
					resMap.put("errorMessage", "buy things you sell? you idiot!");
					return resMap;
				}
				
				// create order
				ProductOrder order = new ProductOrder();
				order.setServiceId((String)reqMap.get("serviceId"));
				order.setBuyerId(phoneNumber);
				order.setSellerId(service.getUserId());
				order.setBuyerName((String)reqMap.get("buyerName"));
				order.setBuyerPhoneNumber((String)reqMap.get("buyerPhoneNumber"));
				if (null != service.getEnterprise()) {
					order.setSellerName(service.getEnterprise());
				} else {
					order.setSellerName(service.getIndividual());
				}
				order.setSellerPhoneNumber(service.getUserId());
				order.setBuyerAddress((String)reqMap.get("buyerAddress"));
				order.setVolume(Integer.valueOf((String)reqMap.get("volume")));
				if (null != reqMap.get("buyerNote")) {
					order.setBuyerNote((String)reqMap.get("buyerNote"));
				}
				order.setUnitPrice(service.getPrice());
				order.setName(service.getName());
				order.setTitle(service.getTitle());
				order.setCategory(service.getCategory());
				order.setOrigin(service.getOriginProvince()+service.getOriginCity()+service.getOriginDistrict());
				order.setManInChargeName(service.getManInChargeName());
				order.setManInChargePhoneNumber(service.getManInChargePhoneNumber());
				order.setStatus(ProductOrder.STATUS_UNPAID);
				order.setLastUpdateTime(LocalDateTime.now());
				order.setCreationDate(LocalDateTime.now());
				order.setProductPhotoId(service.getProductPhotoId());
				order.setOrderNumber(userOrder.getId());
				mongoTemplate.save(order);
				totalAmount += order.getUnitPrice().floatValue()*order.getVolume().intValue();
				effectiveProductServiceList.add(order.toMap((String)reqMap.get("buyerPhoneNumber")));
			}
		}
		
		// update user order
		Query orderQuery = new Query();
		Criteria idCriteria = Criteria.where("id").is(userOrder.getId());
		orderQuery.addCriteria(idCriteria);
		Update orderUpdate = new Update();
		if (null != req.getVehicle()) {
			orderUpdate.set("vehicle", effectiveVehicleServiceList);
		}
		if (null != req.getWarehouse()) {
			orderUpdate.set("warehouse", effectiveWarehouseServiceList);
		}
		if (null != req.getProduct()) {
			orderUpdate.set("product", effectiveProductServiceList);
		}
		orderUpdate.set("creationDate", new Date());
		mongoTemplate.updateFirst(orderQuery, orderUpdate, UserOrder.class);
		
		// remove ordered services from user's cart
		List<String> serviceIdList = new ArrayList<String>();
		if (null != req.getVehicle()) {
			for (Map<String, Object> theMap : effectiveVehicleServiceList) {
				serviceIdList.add((String)theMap.get("serviceId"));
			}
		}
		if (null != req.getWarehouse()) {
			for (Map<String, Object> theMap : effectiveWarehouseServiceList) {
				serviceIdList.add((String)theMap.get("serviceId"));
			}
		}
		if (null != req.getProduct()) {
			for (Map<String, Object> theMap : effectiveProductServiceList) {
				serviceIdList.add((String)theMap.get("serviceId"));
			}
		}
		cartDataManager.deleteServicesFromCartById(phoneNumber, serviceIdList);
		
		if ((0==effectiveVehicleServiceList.size()) && (0==effectiveWarehouseServiceList.size()) && (0==effectiveProductServiceList.size())) {
			return null;
		}
		
		// start alipay handling
		String outTradeNo = userOrder.getId();
		String subject = "中国冷链订单";
		String totalFee = String.format("%.02f", Float.valueOf(totalAmount*AppConfigurator.priceFactor));
//		String totalFee = "0.01";
		String body = "中国冷链订单";
		Map<String, String> alipayReqMap = new HashMap<String, String>();
		alipayReqMap.put("service", AlipayConfig.service);
		alipayReqMap.put("partner", AlipayConfig.partner);
		alipayReqMap.put("seller_id", AlipayConfig.seller_id);
		alipayReqMap.put("_input_charset", AlipayConfig.input_charset);
		alipayReqMap.put("payment_type", AlipayConfig.payment_type);
		alipayReqMap.put("notify_url", AlipayConfig.notify_url);
		alipayReqMap.put("return_url", AlipayConfig.return_url);
		alipayReqMap.put("anti_phishing_key", AlipayConfig.anti_phishing_key);
		alipayReqMap.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);
		alipayReqMap.put("out_trade_no", outTradeNo);
		alipayReqMap.put("subject", subject);
		alipayReqMap.put("total_fee", totalFee);
//		alipayReqMap.put("total_fee", "0.01");
		alipayReqMap.put("it_b_pay", "20m");
		alipayReqMap.put("body", body);
		
		String paymentUrl = AlipaySubmit.buildGetRequest(alipayReqMap);
		logger.info("GET URL - " + paymentUrl);
		Map<String, String> map = new HashMap<String, String>();
		map.put("paymentUrl", paymentUrl);
		map.put("orderId", userOrder.getId());
		
		return map;
	}
	
	public String orderPaymentRequest(String phoneNumber, OrderPaymentRequestRequest req) {
		if (0 == req.getLocator().compareTo("vehicle")) {
			Query query = new Query();
			Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
			Criteria userCriteria = Criteria.where("buyerId").is(phoneNumber);
			Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_UNPAID);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
			query.addCriteria(andCriteria);
			CompleteVehicleOrder order = mongoTemplate.findOne(query, CompleteVehicleOrder.class);
			if (null == order) {
				return null;
			}
			String outTradeNo = order.getId();
			String subject = "中国冷链订单";
			String totalFee = String.format("%.02f", Float.valueOf(order.getPrice().floatValue()*AppConfigurator.priceFactor));
//			String totalFee = "0.01";
			String body = "车辆订单";
			Map<String, String> alipayReqMap = new HashMap<String, String>();
			alipayReqMap.put("service", AlipayConfig.service);
			alipayReqMap.put("partner", AlipayConfig.partner);
			alipayReqMap.put("seller_id", AlipayConfig.seller_id);
			alipayReqMap.put("_input_charset", AlipayConfig.input_charset);
			alipayReqMap.put("payment_type", AlipayConfig.payment_type);
			alipayReqMap.put("notify_url", AlipayConfig.per_order_notify_url);
			alipayReqMap.put("return_url", AlipayConfig.return_url);
			alipayReqMap.put("anti_phishing_key", AlipayConfig.anti_phishing_key);
			alipayReqMap.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);
			alipayReqMap.put("out_trade_no", outTradeNo);
			alipayReqMap.put("subject", subject);
			alipayReqMap.put("total_fee", totalFee);
			alipayReqMap.put("it_b_pay", "20m");
			alipayReqMap.put("body", body);
			String paymentUrl = AlipaySubmit.buildGetRequest(alipayReqMap);
			Update update = new Update();
			update.set("paymentUrl", paymentUrl);
			update.set("lastUpdateTime", LocalDateTime.now());
			if (!mongoTemplate.updateFirst(query, update, CompleteVehicleOrder.class).isUpdateOfExisting()) {
				logger.error("failed to generate payment url for vehicle order: " + req.getOrderId());
				return null;
			}
			return paymentUrl;
		} else if (0 == req.getLocator().compareTo("warehouse")) {
			Query query = new Query();
			Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
			Criteria userCriteria = Criteria.where("buyerId").is(phoneNumber);
			Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_UNPAID);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
			query.addCriteria(andCriteria);
			WarehouseOrder order = mongoTemplate.findOne(query, WarehouseOrder.class);
			if (null == order) {
				return null;
			}
			if (null != order.getPaymentUrlForAlipay()) {
				return order.getPaymentUrlForAlipay();
			}
			String outTradeNo = order.getId();
			String subject = "中国冷链订单";
			int days = DateUtil.daysBetween(order.getStartDate(), order.getUntilDate());
			String totalFee = String.format("%.02f", Float.valueOf(order.getUnitPrice().floatValue()*order.getVolume().intValue()*days*AppConfigurator.priceFactor));
//			String totalFee = "0.01";
			String body = "冷库订单";
			Map<String, String> alipayReqMap = new HashMap<String, String>();
			alipayReqMap.put("service", AlipayConfig.service);
			alipayReqMap.put("partner", AlipayConfig.partner);
			alipayReqMap.put("seller_id", AlipayConfig.seller_id);
			alipayReqMap.put("_input_charset", AlipayConfig.input_charset);
			alipayReqMap.put("payment_type", AlipayConfig.payment_type);
			alipayReqMap.put("notify_url", AlipayConfig.per_order_notify_url);
			alipayReqMap.put("return_url", AlipayConfig.return_url);
			alipayReqMap.put("anti_phishing_key", AlipayConfig.anti_phishing_key);
			alipayReqMap.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);
			alipayReqMap.put("out_trade_no", outTradeNo);
			alipayReqMap.put("subject", subject);
			alipayReqMap.put("total_fee", totalFee);
			alipayReqMap.put("it_b_pay", "20m");
			alipayReqMap.put("body", body);
			String paymentUrl = AlipaySubmit.buildGetRequest(alipayReqMap);
			Update paymentUpdate = new Update();
			paymentUpdate.set("lastUpdateTime", LocalDateTime.now());
			paymentUpdate.set("paymentUrlForAlipay", paymentUrl);
			WriteResult paymentResult = mongoTemplate.updateFirst(query, paymentUpdate, WarehouseOrder.class);
			if (!paymentResult.isUpdateOfExisting()) {
				logger.error("failed to generate payment url for warehouse order: " + req.getOrderId());
				return null;
			}
			return paymentUrl;
		} else if (0 == req.getLocator().compareTo("product")) {
			Query query = new Query();
			Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
			Criteria userCriteria = Criteria.where("buyerId").is(phoneNumber);
			Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_UNPAID);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
			query.addCriteria(andCriteria);
			ProductOrder order = mongoTemplate.findOne(query, ProductOrder.class);
			if (null == order) {
				return null;
			}
			String outTradeNo = order.getId();
			String subject = "中国冷链订单";
			String totalFee = String.format("%.02f", Float.valueOf(order.getUnitPrice().floatValue()*order.getVolume().intValue()*AppConfigurator.priceFactor));
//			String totalFee = "0.01";
			String body = "货物订单";
			Map<String, String> alipayReqMap = new HashMap<String, String>();
			alipayReqMap.put("service", AlipayConfig.service);
			alipayReqMap.put("partner", AlipayConfig.partner);
			alipayReqMap.put("seller_id", AlipayConfig.seller_id);
			alipayReqMap.put("_input_charset", AlipayConfig.input_charset);
			alipayReqMap.put("payment_type", AlipayConfig.payment_type);
			alipayReqMap.put("notify_url", AlipayConfig.per_order_notify_url);
			alipayReqMap.put("return_url", AlipayConfig.return_url);
			alipayReqMap.put("anti_phishing_key", AlipayConfig.anti_phishing_key);
			alipayReqMap.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);
			alipayReqMap.put("out_trade_no", outTradeNo);
			alipayReqMap.put("subject", subject);
			alipayReqMap.put("total_fee", totalFee);
			alipayReqMap.put("it_b_pay", "20m");
			alipayReqMap.put("body", body);
			String paymentUrl = AlipaySubmit.buildGetRequest(alipayReqMap);
			Update update = new Update();
			update.set("paymentUrl", paymentUrl);
			update.set("lastUpdateTime", LocalDateTime.now());
			if (!mongoTemplate.updateFirst(query, update, ProductOrder.class).isUpdateOfExisting()) {
				logger.error("failed to generate payment url for product order: " + req.getOrderId());
				return null;
			}
			return paymentUrl;
		}
		
		return null;
	}
	
	public boolean deleteOrder(String phoneNumber, DeleteOrderRequest req) {
		if (0 == req.getLocator().compareTo("vehicle")) {
			Query query = new Query();
			Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
			Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
			Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_UNPAID);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(idCriteria, buyerCriteria, statusCriteria);
			query.addCriteria(andCriteria);
			WriteResult result = mongoTemplate.remove(query, CompleteVehicleOrder.class);
			if (1 != result.getN()) {
				return false;
			} else {
				return true;
			}
		} else if (0 == req.getLocator().compareTo("warehouse")) {
			Query query = new Query();
			Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
			Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
			Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_UNPAID);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(idCriteria, buyerCriteria, statusCriteria);
			query.addCriteria(andCriteria);
			WriteResult result = mongoTemplate.remove(query, WarehouseOrder.class);
			if (1 != result.getN()) {
				return false;
			} else {
				return true;
			}
		} else if (0 == req.getLocator().compareTo("product")) {
			Query query = new Query();
			Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
			Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
			Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_UNPAID);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(idCriteria, buyerCriteria, statusCriteria);
			query.addCriteria(andCriteria);
			WriteResult result = mongoTemplate.remove(query, ProductOrder.class);
			if (1 != result.getN()) {
				return false;
			} else {
				return true;
			}
		}
		
		return false;
	}
	
	public void orderPaid(String orderNumber, String alipayOrderNumber) {
		Query orderQuery = new Query();
		Criteria orderIdCriteria = Criteria.where("id").is(orderNumber);
		orderQuery.addCriteria(orderIdCriteria);
		UserOrder userOrder = mongoTemplate.findOne(orderQuery, UserOrder.class);
		if (null == userOrder) {
			logger.error("orderPaid - cannot locate order for " + orderNumber);
			return;
		}
		
		if (null != userOrder.getVehicle()) {
			Query vehicleQuery = new Query();
			Criteria orderCriteria = Criteria.where("orderNumber").is(orderNumber);
			Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_UNPAID);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(orderCriteria, statusCriteria);
			vehicleQuery.addCriteria(andCriteria);
			List<CompleteVehicleOrder> list = mongoTemplate.find(vehicleQuery, CompleteVehicleOrder.class);
			Update vehicleUpdate = new Update();
			vehicleUpdate.set("status", CompleteVehicleOrder.STATUS_TRANSPORT_PENDING);
			vehicleUpdate.set("alipayOrderNumber", alipayOrderNumber);
			vehicleUpdate.set("paidDate", new Date());
			WriteResult vehicleResult = mongoTemplate.updateMulti(vehicleQuery, vehicleUpdate, CompleteVehicleOrder.class);
			if (vehicleResult.getN() != userOrder.getVehicle().size()) {
				logger.error("orderPaid - not all orders got updated, identified by order number: " + orderNumber);
			} else {
				for (CompleteVehicleOrder vehicleOrder : list) {
					PlatformRealtimeInfo realtimeInfo = new PlatformRealtimeInfo();
					realtimeInfo.setModule(PlatformRealtimeInfo.MODULE_ORDER);
					realtimeInfo.setIdentifier(PlatformRealtimeInfo.IDENTIFIER_VEHICLE);
					realtimeInfo.setVehicleOrderLicensePlate(vehicleOrder.getLengthSpec());
					realtimeInfo.setVehicleOrderNumber(alipayOrderNumber);
					realtimeInfo.setVehicleOrderRoute(vehicleOrder.getRouteOrigin()+"-"+vehicleOrder.getRouteDestination());
					realtimeInfo.setVehicleOrderBuyer(vehicleOrder.getBuyerName());
					realtimeInfo.setVehicleOrderBuyerPhoneNumber(vehicleOrder.getBuyerPhoneNumber());
					realtimeInfo.setCreationDate(LocalDateTime.now());
					mongoTemplate.save(realtimeInfo);
					
					User user = identityDataManager.findUser(vehicleOrder.getSellerPhoneNumber());
					if (null != user) {
						String userDevice = user.getMobileDeviceOS();
						if (null != userDevice) {
							// signed in from mobile device
							if (0 == userDevice.compareTo("Android")) {
								if (null == user.getDeviceToken()) {
									logger.warn("device token not updated for user - " + user.getPhoneNumber());
								} else {
									try {
										PushClient client = new PushClient();
										AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
										unicast.setDeviceToken(user.getDeviceToken());
										unicast.setTicker("中国冷链订单");
										unicast.setTitle("您有新的车辆订单");
										unicast.setText("恭喜您有新的车辆订单，请及时调整线上资源。");
										unicast.goAppAfterOpen();
										unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
										unicast.setProductionMode();
										client.send(unicast);
									} catch (Exception e) {
										logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
									}
								}
							} else if (0 == userDevice.compareTo("iOS")) {
								if (null == user.getDeviceToken()) {
									logger.warn("device token not updated for user - " + user.getPhoneNumber());
								} else {
									try {
										PushClient client = new PushClient();
										IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
										unicast.setDeviceToken(user.getDeviceToken());
										unicast.setAlert("您有新的车辆订单");
										unicast.setBadge(0);
										unicast.setSound("default");
										if (MessageController.isRunningOnProduction) {
											unicast.setProductionMode();
										} else {
											unicast.setTestMode();
										}
										client.send(unicast);
									} catch (Exception e) {
										logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
									}
								}
							}
						}
					}
				}
			}
		}
		if (null != userOrder.getWarehouse()) {
			Query warehouseQuery = new Query();
			Criteria orderCriteria = Criteria.where("orderNumber").is(orderNumber);
			Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_UNPAID);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(orderCriteria, statusCriteria);
			warehouseQuery.addCriteria(andCriteria);
			List<WarehouseOrder> list = mongoTemplate.find(warehouseQuery, WarehouseOrder.class);
			Update warehouseUpdate = new Update();
			warehouseUpdate.set("status", WarehouseOrder.STATUS_LOAD_PENDING);
			warehouseUpdate.set("alipayOrderNumber", alipayOrderNumber);
			warehouseUpdate.set("paidDate", new Date());
			WriteResult warehouseResult = mongoTemplate.updateMulti(warehouseQuery, warehouseUpdate, WarehouseOrder.class);
			if (warehouseResult.getN() != userOrder.getWarehouse().size()) {
				logger.error("orderPaid - not all orders got updated, identified by order number: " + orderNumber);
			} else {
				for (WarehouseOrder warehouseOrder : list) {
					PlatformRealtimeInfo realtimeInfo = new PlatformRealtimeInfo();
					realtimeInfo.setModule(PlatformRealtimeInfo.MODULE_ORDER);
					realtimeInfo.setIdentifier(PlatformRealtimeInfo.IDENTIFIER_WAREHOUSE);
					realtimeInfo.setWarehouseOrderName(warehouseOrder.getWarehouseName());
					realtimeInfo.setWarehouseOrderNumber(alipayOrderNumber);
					realtimeInfo.setWarehouseOrderVolume(warehouseOrder.getVolume().toString());
					realtimeInfo.setWarehouseOrderBuyer(warehouseOrder.getBuyerName());
					realtimeInfo.setWarehouseOrderBuyerPhoneNumber(warehouseOrder.getBuyerPhoneNumber());
					realtimeInfo.setCreationDate(LocalDateTime.now());
					mongoTemplate.save(realtimeInfo);
					
					User user = identityDataManager.findUser(warehouseOrder.getSellerPhoneNumber());
					if (null != user) {
						String userDevice = user.getMobileDeviceOS();
						if (null != userDevice) {
							// signed in from mobile device
							if (0 == userDevice.compareTo("Android")) {
								if (null == user.getDeviceToken()) {
									logger.warn("device token not updated for user - " + user.getPhoneNumber());
								} else {
									try {
										PushClient client = new PushClient();
										AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
										unicast.setDeviceToken(user.getDeviceToken());
										unicast.setTicker("中国冷链订单");
										unicast.setTitle("您有新的冷库订单");
										unicast.setText("恭喜您有新的冷库订单，请及时调整线上资源。");
										unicast.goAppAfterOpen();
										unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
										unicast.setProductionMode();
										client.send(unicast);
									} catch (Exception e) {
										logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
									}
								}
							} else if (0 == userDevice.compareTo("iOS")) {
								if (null == user.getDeviceToken()) {
									logger.warn("device token not updated for user - " + user.getPhoneNumber());
								} else {
									try {
										PushClient client = new PushClient();
										IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
										unicast.setDeviceToken(user.getDeviceToken());
										unicast.setAlert("您有新的冷库订单");
										unicast.setBadge(0);
										unicast.setSound("default");
										if (MessageController.isRunningOnProduction) {
											unicast.setProductionMode();
										} else {
											unicast.setTestMode();
										}
										client.send(unicast);
									} catch (Exception e) {
										logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
									}
								}
							}
						}
					}
				}
			}
		}
		if (null != userOrder.getProduct()) {
			Query productQuery = new Query();
			Criteria orderCriteria = Criteria.where("orderNumber").is(orderNumber);
			Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_UNPAID);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(orderCriteria, statusCriteria);
			productQuery.addCriteria(andCriteria);
			List<ProductOrder> list = mongoTemplate.find(productQuery, ProductOrder.class);
			Update productUpdate = new Update();
			productUpdate.set("status", ProductOrder.STATUS_SHIP_PENDING);
			productUpdate.set("alipayOrderNumber", alipayOrderNumber);
			productUpdate.set("paidDate", new Date());
			WriteResult productResult = mongoTemplate.updateMulti(productQuery, productUpdate, ProductOrder.class);
			if (productResult.getN() != userOrder.getProduct().size()) {
				logger.error("orderPaid - not all orders got updated, identified by order number: " + orderNumber);
			} else {
				for (ProductOrder productOrder : list) {
					PlatformRealtimeInfo realtimeInfo = new PlatformRealtimeInfo();
					realtimeInfo.setModule(PlatformRealtimeInfo.MODULE_ORDER);
					realtimeInfo.setIdentifier(PlatformRealtimeInfo.IDENTIFIER_PRODUCT);
					realtimeInfo.setProductOrderTitle(productOrder.getTitle());
					realtimeInfo.setProductOrderNumber(alipayOrderNumber);
					realtimeInfo.setProductOrderVolume(productOrder.getVolume().toString());
					realtimeInfo.setProductOrderBuyer(productOrder.getBuyerName());
					realtimeInfo.setProductOrderBuyerPhoneNumber(productOrder.getBuyerPhoneNumber());
					realtimeInfo.setCreationDate(LocalDateTime.now());
					mongoTemplate.save(realtimeInfo);
					
					User user = identityDataManager.findUser(productOrder.getSellerPhoneNumber());
					if (null != user) {
						String userDevice = user.getMobileDeviceOS();
						if (null != userDevice) {
							// signed in from mobile device
							if (0 == userDevice.compareTo("Android")) {
								if (null == user.getDeviceToken()) {
									logger.warn("device token not updated for user - " + user.getPhoneNumber());
								} else {
									try {
										PushClient client = new PushClient();
										AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
										unicast.setDeviceToken(user.getDeviceToken());
										unicast.setTicker("中国冷链订单");
										unicast.setTitle("您有新的货物订单");
										unicast.setText("恭喜您有新的货物订单，请及时调整线上资源。");
										unicast.goAppAfterOpen();
										unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
										unicast.setProductionMode();
										client.send(unicast);
									} catch (Exception e) {
										logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
									}
								}
							} else if (0 == userDevice.compareTo("iOS")) {
								if (null == user.getDeviceToken()) {
									logger.warn("device token not updated for user - " + user.getPhoneNumber());
								} else {
									try {
										PushClient client = new PushClient();
										IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
										unicast.setDeviceToken(user.getDeviceToken());
										unicast.setAlert("您有新的货物订单");
										unicast.setBadge(0);
										unicast.setSound("default");
										if (MessageController.isRunningOnProduction) {
											unicast.setProductionMode();
										} else {
											unicast.setTestMode();
										}
										client.send(unicast);
									} catch (Exception e) {
										logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
									}
								}
							}
						}
					}
				}
			}
		}
	}
	
	public void specificOrderPaid(String orderNumber, String alipayOrderNumber) {
		Query orderQuery = new Query();
		Criteria orderIdCriteria = Criteria.where("id").is(orderNumber);
		orderQuery.addCriteria(orderIdCriteria);
		CompleteVehicleOrder vehicleOrder = mongoTemplate.findOne(orderQuery, CompleteVehicleOrder.class);
		if (null != vehicleOrder) {
			Query vehicleQuery = new Query();
			Criteria vehicleIdCriteria = Criteria.where("id").is(orderNumber);
			Criteria vehicleStatusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_UNPAID);
			Criteria vehicleAndCriteria = new Criteria();
			vehicleAndCriteria.andOperator(vehicleIdCriteria, vehicleStatusCriteria);
			vehicleQuery.addCriteria(vehicleAndCriteria);
			Update vehicleUpdate = new Update();
			vehicleUpdate.set("status", CompleteVehicleOrder.STATUS_TRANSPORT_PENDING);
			vehicleUpdate.set("alipayOrderNumber", alipayOrderNumber);
			vehicleUpdate.set("paidDate", new Date());
			WriteResult vehicleResult = mongoTemplate.updateFirst(vehicleQuery, vehicleUpdate, CompleteVehicleOrder.class);
			if (!vehicleResult.isUpdateOfExisting()) {
				logger.error("specificOrderPaid - [vehicle] inconsistent order status for : " + orderNumber);
			} else {
				PlatformRealtimeInfo realtimeInfo = new PlatformRealtimeInfo();
				realtimeInfo.setModule(PlatformRealtimeInfo.MODULE_ORDER);
				realtimeInfo.setIdentifier(PlatformRealtimeInfo.IDENTIFIER_VEHICLE);
				realtimeInfo.setVehicleOrderLicensePlate((String)vehicleOrder.getVehicle().get("licensePlate"));
				realtimeInfo.setVehicleOrderNumber(alipayOrderNumber);
				realtimeInfo.setVehicleOrderRoute(vehicleOrder.getRouteOrigin()+"-"+vehicleOrder.getRouteDestination());
				realtimeInfo.setVehicleOrderBuyer(vehicleOrder.getBuyerName());
				realtimeInfo.setVehicleOrderBuyerPhoneNumber(vehicleOrder.getBuyerPhoneNumber());
				realtimeInfo.setCreationDate(LocalDateTime.now());
				mongoTemplate.save(realtimeInfo);
				
				User user = identityDataManager.findUser(vehicleOrder.getSellerPhoneNumber());
				if (null != user) {
					String userDevice = user.getMobileDeviceOS();
					if (null != userDevice) {
						// signed in from mobile device
						if (0 == userDevice.compareTo("Android")) {
							if (null == user.getDeviceToken()) {
								logger.warn("device token not updated for user - " + user.getPhoneNumber());
							} else {
								try {
									PushClient client = new PushClient();
									AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
									unicast.setDeviceToken(user.getDeviceToken());
									unicast.setTicker("中国冷链订单");
									unicast.setTitle("您有新的车辆订单");
									unicast.setText("恭喜您有新的车辆订单，请及时调整线上资源。");
									unicast.goAppAfterOpen();
									unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
									unicast.setProductionMode();
									client.send(unicast);
								} catch (Exception e) {
									logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
								}
							}
						} else if (0 == userDevice.compareTo("iOS")) {
							if (null == user.getDeviceToken()) {
								logger.warn("device token not updated for user - " + user.getPhoneNumber());
							} else {
								try {
									PushClient client = new PushClient();
									IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
									unicast.setDeviceToken(user.getDeviceToken());
									unicast.setAlert("您有新的车辆订单");
									unicast.setBadge(0);
									unicast.setSound("default");
									if (MessageController.isRunningOnProduction) {
										unicast.setProductionMode();
									} else {
										unicast.setTestMode();
									}
									client.send(unicast);
								} catch (Exception e) {
									logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
								}
							}
						}
					}
				}
			}
			
			return;
		}
		WarehouseOrder warehouseOrder = mongoTemplate.findOne(orderQuery, WarehouseOrder.class);
		if (null != warehouseOrder) {
			Query warehouseQuery = new Query();
			Criteria warehouseIdCriteria = Criteria.where("id").is(orderNumber);
			Criteria warehouseStatusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_UNPAID);
			Criteria warehouseAndCriteria = new Criteria();
			warehouseAndCriteria.andOperator(warehouseIdCriteria, warehouseStatusCriteria);
			warehouseQuery.addCriteria(warehouseAndCriteria);
			Update warehouseUpdate = new Update();
			warehouseUpdate.set("status", WarehouseOrder.STATUS_LOAD_PENDING);
			warehouseUpdate.set("alipayOrderNumber", alipayOrderNumber);
			warehouseUpdate.set("paidDate", new Date());
			WriteResult warehouseResult = mongoTemplate.updateFirst(warehouseQuery, warehouseUpdate, WarehouseOrder.class);
			if (!warehouseResult.isUpdateOfExisting()) {
				logger.error("specificOrderPaid - [warehouse] inconsistent order status for : " + orderNumber);
			} else {
				PlatformRealtimeInfo realtimeInfo = new PlatformRealtimeInfo();
				realtimeInfo.setModule(PlatformRealtimeInfo.MODULE_ORDER);
				realtimeInfo.setIdentifier(PlatformRealtimeInfo.IDENTIFIER_WAREHOUSE);
				realtimeInfo.setWarehouseOrderName(warehouseOrder.getWarehouseName());
				realtimeInfo.setWarehouseOrderNumber(alipayOrderNumber);
				realtimeInfo.setWarehouseOrderVolume(warehouseOrder.getVolume().toString());
				realtimeInfo.setWarehouseOrderBuyer(warehouseOrder.getBuyerName());
				realtimeInfo.setWarehouseOrderBuyerPhoneNumber(warehouseOrder.getBuyerPhoneNumber());
				realtimeInfo.setCreationDate(LocalDateTime.now());
				mongoTemplate.save(realtimeInfo);
				User user = identityDataManager.findUser(warehouseOrder.getSellerPhoneNumber());
				if (null != user) {
					String userDevice = user.getMobileDeviceOS();
					if (null != userDevice) {
						// signed in from mobile device
						if (0 == userDevice.compareTo("Android")) {
							if (null == user.getDeviceToken()) {
								logger.warn("device token not updated for user - " + user.getPhoneNumber());
							} else {
								try {
									PushClient client = new PushClient();
									AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
									unicast.setDeviceToken(user.getDeviceToken());
									unicast.setTicker("中国冷链订单");
									unicast.setTitle("您有新的冷库订单");
									unicast.setText("恭喜您有新的冷库订单，请及时调整线上资源。");
									unicast.goAppAfterOpen();
									unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
									unicast.setProductionMode();
									client.send(unicast);
								} catch (Exception e) {
									logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
								}
							}
						} else if (0 == userDevice.compareTo("iOS")) {
							if (null == user.getDeviceToken()) {
								logger.warn("device token not updated for user - " + user.getPhoneNumber());
							} else {
								try {
									PushClient client = new PushClient();
									IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
									unicast.setDeviceToken(user.getDeviceToken());
									unicast.setAlert("您有新的冷库订单");
									unicast.setBadge(0);
									unicast.setSound("default");
									if (MessageController.isRunningOnProduction) {
										unicast.setProductionMode();
									} else {
										unicast.setTestMode();
									}
									client.send(unicast);
								} catch (Exception e) {
									logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
								}
							}
						}
					}
				}
			}
			
			return;
		}
		ProductOrder productOrder = mongoTemplate.findOne(orderQuery, ProductOrder.class);
		if (null != productOrder) {
			Query productQuery = new Query();
			Criteria productIdCriteria = Criteria.where("id").is(orderNumber);
			Criteria productStatusCriteria = Criteria.where("status").is(ProductOrder.STATUS_UNPAID);
			Criteria productAndCriteria = new Criteria();
			productAndCriteria.andOperator(productIdCriteria, productStatusCriteria);
			productQuery.addCriteria(productAndCriteria);
			Update productUpdate = new Update();
			productUpdate.set("status", ProductOrder.STATUS_SHIP_PENDING);
			productUpdate.set("alipayOrderNumber", alipayOrderNumber);
			productUpdate.set("paidDate", new Date());
			WriteResult productResult = mongoTemplate.updateFirst(productQuery, productUpdate, ProductOrder.class);
			if (!productResult.isUpdateOfExisting()) {
				logger.error("specificOrderPaid - [product] inconsistent order status for : " + orderNumber);
			} else {
				PlatformRealtimeInfo realtimeInfo = new PlatformRealtimeInfo();
				realtimeInfo.setModule(PlatformRealtimeInfo.MODULE_ORDER);
				realtimeInfo.setIdentifier(PlatformRealtimeInfo.IDENTIFIER_PRODUCT);
				realtimeInfo.setProductOrderTitle(productOrder.getTitle());
				realtimeInfo.setProductOrderNumber(alipayOrderNumber);
				realtimeInfo.setProductOrderVolume(productOrder.getVolume().toString());
				realtimeInfo.setProductOrderBuyer(productOrder.getBuyerName());
				realtimeInfo.setProductOrderBuyerPhoneNumber(productOrder.getBuyerPhoneNumber());
				realtimeInfo.setCreationDate(LocalDateTime.now());
				mongoTemplate.save(realtimeInfo);
				User user = identityDataManager.findUser(productOrder.getSellerPhoneNumber());
				if (null != user) {
					String userDevice = user.getMobileDeviceOS();
					if (null != userDevice) {
						// signed in from mobile device
						if (0 == userDevice.compareTo("Android")) {
							if (null == user.getDeviceToken()) {
								logger.warn("device token not updated for user - " + user.getPhoneNumber());
							} else {
								try {
									PushClient client = new PushClient();
									AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
									unicast.setDeviceToken(user.getDeviceToken());
									unicast.setTicker("中国冷链订单");
									unicast.setTitle("您有新的货物订单");
									unicast.setText("恭喜您有新的货物订单，请及时调整线上资源。");
									unicast.goAppAfterOpen();
									unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
									unicast.setProductionMode();
									client.send(unicast);
								} catch (Exception e) {
									logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
								}
							}
						} else if (0 == userDevice.compareTo("iOS")) {
							if (null == user.getDeviceToken()) {
								logger.warn("device token not updated for user - " + user.getPhoneNumber());
							} else {
								try {
									PushClient client = new PushClient();
									IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
									unicast.setDeviceToken(user.getDeviceToken());
									unicast.setAlert("您有新的货物订单");
									unicast.setBadge(0);
									unicast.setSound("default");
									if (MessageController.isRunningOnProduction) {
										unicast.setProductionMode();
									} else {
										unicast.setTestMode();
									}
									client.send(unicast);
								} catch (Exception e) {
									logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
								}
							}
						}
					}
				}
			}
			
			return;
		}
		
		logger.error("specificOrderPaid - order not found for : " + orderNumber);
	}
	
	public List<Map<String, Object>> unpaidWarehouseOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_UNPAID);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<WarehouseOrder> list = mongoTemplate.find(query, WarehouseOrder.class);
		for (WarehouseOrder order : list) {
			result.add(order.toMap(phoneNumber));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> loadedWarehouseOrders(String phoneNumber) {		
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_LOADED);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<WarehouseOrder> list = mongoTemplate.find(query, WarehouseOrder.class);
		for (WarehouseOrder order : list) {
			result.add(order.toMap(phoneNumber));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> reviewPendingWarehouseOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_REVIEW_PENDING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<WarehouseOrder> list = mongoTemplate.find(query, WarehouseOrder.class);
		for (WarehouseOrder order : list) {
			result.add(order.toMap(phoneNumber));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> unpaidProductOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_UNPAID);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<ProductOrder> list = mongoTemplate.find(query, ProductOrder.class);
		for (ProductOrder order : list) {
			result.add(order.toMap(phoneNumber));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> unpaidVehicleOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_UNPAID);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		Sort.Order order = new Sort.Order(Sort.Direction.DESC, "creationDate");
		Sort sort = new Sort(order);
		query.with(sort);
		
		List<CompleteVehicleOrder> list = mongoTemplate.find(query, CompleteVehicleOrder.class);
		for (CompleteVehicleOrder vehicleOrder : list) {
			result.add(vehicleOrder.toMap(phoneNumber));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> loadPendingWarehouseOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_LOAD_PENDING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<WarehouseOrder> list = mongoTemplate.find(query, WarehouseOrder.class);
		for (WarehouseOrder order : list) {
			result.add(order.toMap(null));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> shipPendingProductOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_SHIP_PENDING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<ProductOrder> list = mongoTemplate.find(query, ProductOrder.class);
		for (ProductOrder order : list) {
			result.add(order.toMap(null));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> transportPendingVehicleOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_TRANSPORT_PENDING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		Sort.Order order = new Sort.Order(Sort.Direction.DESC, "creationDate");
		Sort sort = new Sort(order);
		query.with(sort);
		
		List<CompleteVehicleOrder> list = mongoTemplate.find(query, CompleteVehicleOrder.class);
		for (CompleteVehicleOrder vehicleOrder : list) {
			result.add(vehicleOrder.toMap(null));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> transportOnGoingVehicleOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_TRANSPORT_ONGOING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		Sort.Order order = new Sort.Order(Sort.Direction.DESC, "creationDate");
		Sort sort = new Sort(order);
		query.with(sort);
		
		List<CompleteVehicleOrder> list = mongoTemplate.find(query, CompleteVehicleOrder.class);
		for (CompleteVehicleOrder vehicleOrder : list) {
			result.add(vehicleOrder.toMap(null));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> reviewPendingVehicleOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_REVIEW_PENDING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		Sort.Order order = new Sort.Order(Sort.Direction.DESC, "creationDate");
		Sort sort = new Sort(order);
		query.with(sort);
		
		List<CompleteVehicleOrder> list = mongoTemplate.find(query, CompleteVehicleOrder.class);
		for (CompleteVehicleOrder vehicleOrder : list) {
			result.add(vehicleOrder.toMap(null));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> shippedProductOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_SHIPPED);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<ProductOrder> list = mongoTemplate.find(query, ProductOrder.class);
		for (ProductOrder order : list) {
			result.add(order.toMap(null));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> reviewPendingProductOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_REVIEW_PENDING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<ProductOrder> list = mongoTemplate.find(query, ProductOrder.class);
		for (ProductOrder order : list) {
			result.add(order.toMap(null));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> completedProductOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria completeCriteria = Criteria.where("status").is(ProductOrder.STATUS_COMPLETED);
		Criteria cancelCriteria = Criteria.where("status").is(ProductOrder.STATUS_CANCELED);
		Criteria statusCriteria = new Criteria();
		statusCriteria.orOperator(completeCriteria, cancelCriteria);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<ProductOrder> list = mongoTemplate.find(query, ProductOrder.class);
		for (ProductOrder order : list) {
			result.add(order.toMap(null));
		}
		
		return result;
	}
	
	public void overallProductOrderCounting(String phoneNumber, Map<String, String> map, String q) {
		Criteria roleOrCriteria = null;
		if (0 == q.compareTo("buyer")) {
			roleOrCriteria = Criteria.where("buyerId").is(phoneNumber);
		} else {
			roleOrCriteria = Criteria.where("sellerId").is(phoneNumber);
		}
		
		Query unpaidQuery = new Query();
		Criteria unpaidStatusCriteria = Criteria.where("status").is(ProductOrder.STATUS_UNPAID);
		Criteria unpaidAndCriteria = new Criteria();
		unpaidAndCriteria.andOperator(unpaidStatusCriteria, roleOrCriteria);
		unpaidQuery.addCriteria(unpaidAndCriteria);
		long unpaidCount = mongoTemplate.count(unpaidQuery, ProductOrder.class);
		map.put("unpaid", Long.valueOf(unpaidCount).toString());
		
		Query shipPendingQuery = new Query();
		Criteria shipPendingStatusCriteria = Criteria.where("status").is(ProductOrder.STATUS_SHIP_PENDING);
		Criteria shipPendingAndCriteria = new Criteria();
		shipPendingAndCriteria.andOperator(shipPendingStatusCriteria, roleOrCriteria);
		shipPendingQuery.addCriteria(shipPendingAndCriteria);
		long shipPendingCount = mongoTemplate.count(shipPendingQuery, ProductOrder.class);
		map.put("shipPending", Long.valueOf(shipPendingCount).toString());
		
		Query shippedQuery = new Query();
		Criteria shippedStatusCriteria = Criteria.where("status").is(ProductOrder.STATUS_SHIPPED);
		Criteria shippedAndCriteria = new Criteria();
		shippedAndCriteria.andOperator(shippedStatusCriteria, roleOrCriteria);
		shippedQuery.addCriteria(shippedAndCriteria);
		long shippedCount = mongoTemplate.count(shippedQuery, ProductOrder.class);
		map.put("shipped", Long.valueOf(shippedCount).toString());
		
		Query reviewPendingQuery = new Query();
		Criteria reviewPendingStatusCriteria = Criteria.where("status").is(ProductOrder.STATUS_REVIEW_PENDING);
		Criteria reviewPendingAndCriteria = new Criteria();
		reviewPendingAndCriteria.andOperator(reviewPendingStatusCriteria, roleOrCriteria);
		reviewPendingQuery.addCriteria(reviewPendingAndCriteria);
		long reviewPendingCount = mongoTemplate.count(reviewPendingQuery, ProductOrder.class);
		map.put("reviewPending", Long.valueOf(reviewPendingCount).toString());
		
		Query completedQuery = new Query();
		Criteria completeCriteria = Criteria.where("status").is(ProductOrder.STATUS_COMPLETED);
		Criteria cancelCriteria = Criteria.where("status").is(ProductOrder.STATUS_CANCELED);
		Criteria statusCriteria = new Criteria();
		statusCriteria.orOperator(completeCriteria, cancelCriteria);
		Criteria completedAndCriteria = new Criteria();
		completedAndCriteria.andOperator(statusCriteria, roleOrCriteria);
		completedQuery.addCriteria(completedAndCriteria);
		long completedCount = mongoTemplate.count(completedQuery, ProductOrder.class);
		map.put("completed", Long.valueOf(completedCount).toString());
	}
	
	public void overallWarehouseOrderCounting(String phoneNumber, Map<String, String> map, String q) {
		Criteria roleOrCriteria = null;
		if (0 == q.compareTo("buyer")) {
			roleOrCriteria = Criteria.where("buyerId").is(phoneNumber);
		} else {
			roleOrCriteria = Criteria.where("sellerId").is(phoneNumber);
		}
		
		Query unpaidQuery = new Query();
		Criteria unpaidStatusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_UNPAID);
		Criteria unpaidAndCriteria = new Criteria();
		unpaidAndCriteria.andOperator(unpaidStatusCriteria, roleOrCriteria);
		unpaidQuery.addCriteria(unpaidAndCriteria);
		long unpaidCount = mongoTemplate.count(unpaidQuery, WarehouseOrder.class);
		map.put("unpaid", Long.valueOf(unpaidCount).toString());
		
		Query shipPendingQuery = new Query();
		Criteria shipPendingStatusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_LOAD_PENDING);
		Criteria shipPendingAndCriteria = new Criteria();
		shipPendingAndCriteria.andOperator(shipPendingStatusCriteria, roleOrCriteria);
		shipPendingQuery.addCriteria(shipPendingAndCriteria);
		long shipPendingCount = mongoTemplate.count(shipPendingQuery, WarehouseOrder.class);
		map.put("loadPending", Long.valueOf(shipPendingCount).toString());
		
		Query shippedQuery = new Query();
		Criteria shippedStatusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_LOADED);
		Criteria shippedAndCriteria = new Criteria();
		shippedAndCriteria.andOperator(shippedStatusCriteria, roleOrCriteria);
		shippedQuery.addCriteria(shippedAndCriteria);
		long shippedCount = mongoTemplate.count(shippedQuery, WarehouseOrder.class);
		map.put("loaded", Long.valueOf(shippedCount).toString());
		
		Query reviewPendingQuery = new Query();
		Criteria reviewPendingStatusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_REVIEW_PENDING);
		Criteria reviewPendingAndCriteria = new Criteria();
		reviewPendingAndCriteria.andOperator(reviewPendingStatusCriteria, roleOrCriteria);
		reviewPendingQuery.addCriteria(reviewPendingAndCriteria);
		long reviewPendingCount = mongoTemplate.count(reviewPendingQuery, WarehouseOrder.class);
		map.put("reviewPending", Long.valueOf(reviewPendingCount).toString());
		
		Query completedQuery = new Query();
		Criteria completeCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_COMPLETED);
		Criteria cancelCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_CANCELED);
		Criteria statusCriteria = new Criteria();
		statusCriteria.orOperator(completeCriteria, cancelCriteria);
		Criteria completedAndCriteria = new Criteria();
		completedAndCriteria.andOperator(statusCriteria, roleOrCriteria);
		completedQuery.addCriteria(completedAndCriteria);
		long completedCount = mongoTemplate.count(completedQuery, WarehouseOrder.class);
		map.put("completed", Long.valueOf(completedCount).toString());
	}
	
	public void overallVehicleOrderCounting(String phoneNumber, Map<String, String> map, String q) {
		Criteria roleOrCriteria = null;
		if (0 == q.compareTo("buyer")) {
			roleOrCriteria = Criteria.where("buyerId").is(phoneNumber);
		} else {
			roleOrCriteria = Criteria.where("sellerId").is(phoneNumber);
		}
		
		Query unpaidQuery = new Query();
		Criteria unpaidStatusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_UNPAID);
		Criteria unpaidAndCriteria = new Criteria();
		unpaidAndCriteria.andOperator(unpaidStatusCriteria, roleOrCriteria);
		unpaidQuery.addCriteria(unpaidAndCriteria);
		long unpaidCount = mongoTemplate.count(unpaidQuery, CompleteVehicleOrder.class);
		map.put("unpaid", Long.valueOf(unpaidCount).toString());
		
		Query shipPendingQuery = new Query();
		Criteria shipPendingStatusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_TRANSPORT_PENDING);
		Criteria shipPendingAndCriteria = new Criteria();
		shipPendingAndCriteria.andOperator(shipPendingStatusCriteria, roleOrCriteria);
		shipPendingQuery.addCriteria(shipPendingAndCriteria);
		long shipPendingCount = mongoTemplate.count(shipPendingQuery, CompleteVehicleOrder.class);
		map.put("transportPending", Long.valueOf(shipPendingCount).toString());
		
		Query shippedQuery = new Query();
		Criteria shippedStatusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_TRANSPORT_ONGOING);
		Criteria shippedAndCriteria = new Criteria();
		shippedAndCriteria.andOperator(shippedStatusCriteria, roleOrCriteria);
		shippedQuery.addCriteria(shippedAndCriteria);
		long shippedCount = mongoTemplate.count(shippedQuery, CompleteVehicleOrder.class);
		map.put("transportOngoing", Long.valueOf(shippedCount).toString());
		
		Query reviewPendingQuery = new Query();
		Criteria reviewPendingStatusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_REVIEW_PENDING);
		Criteria reviewPendingAndCriteria = new Criteria();
		reviewPendingAndCriteria.andOperator(reviewPendingStatusCriteria, roleOrCriteria);
		reviewPendingQuery.addCriteria(reviewPendingAndCriteria);
		long reviewPendingCount = mongoTemplate.count(reviewPendingQuery, CompleteVehicleOrder.class);
		map.put("reviewPending", Long.valueOf(reviewPendingCount).toString());
		
		Query completedQuery = new Query();
		Criteria completeCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_COMPLETED);
		Criteria cancelCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_CANCELED);
		Criteria statusCriteria = new Criteria();
		statusCriteria.orOperator(completeCriteria, cancelCriteria);
		Criteria completedAndCriteria = new Criteria();
		completedAndCriteria.andOperator(statusCriteria, roleOrCriteria);
		completedQuery.addCriteria(completedAndCriteria);
		long completedCount = mongoTemplate.count(completedQuery, CompleteVehicleOrder.class);
		map.put("completed", Long.valueOf(completedCount).toString());
	}
	
	public List<Map<String, Object>> completedVehicleOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria completeCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_COMPLETED);
		Criteria cancelCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_CANCELED);
		Criteria statusCriteria = new Criteria();
		statusCriteria.orOperator(completeCriteria, cancelCriteria);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		Sort.Order order = new Sort.Order(Sort.Direction.DESC, "creationDate");
		Sort sort = new Sort(order);
		query.with(sort);
		
		List<CompleteVehicleOrder> list = mongoTemplate.find(query, CompleteVehicleOrder.class);
		for (CompleteVehicleOrder vehicleOrder : list) {
			result.add(vehicleOrder.toMap(null));
		}
		
		return result;
	}
	
	public List<Map<String, Object>> completedWarehouseOrders(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria buyerCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria sellerCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orCriteria = new Criteria();
		orCriteria.orOperator(buyerCriteria, sellerCriteria);
		Criteria completeCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_COMPLETED);
		Criteria cancelCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_CANCELED);
		Criteria statusCriteria = new Criteria();
		statusCriteria.orOperator(completeCriteria, cancelCriteria);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(statusCriteria, orCriteria);
		query.addCriteria(andCriteria);
		
		List<WarehouseOrder> list = mongoTemplate.find(query, WarehouseOrder.class);
		for (WarehouseOrder order : list) {
			result.add(order.toMap(null));
		}
		
		return result;
	}
	
	public boolean sellerShippedProductOrder(String phoneNumber, String orderId) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(orderId);
		Criteria userCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_SHIP_PENDING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
		query.addCriteria(andCriteria);
		ProductOrder productOrder = mongoTemplate.findOne(query, ProductOrder.class);
		
		Update update = new Update();
		update.set("status", ProductOrder.STATUS_SHIPPED);
		
		WriteResult result = mongoTemplate.updateFirst(query, update, ProductOrder.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		User user = identityDataManager.findUser(productOrder.getBuyerPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链订单");
							unicast.setTitle("您的货物订单已发货");
							unicast.setText("您的货物订单已发货，请及时确认收货。");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("您的货物订单已发货");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}
		
		return true;
	}
	
	public boolean buyerReceivedProductOrder(String phoneNumber, String orderId) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(orderId);
		Criteria userCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_SHIPPED);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
		query.addCriteria(andCriteria);
		ProductOrder productOrder = mongoTemplate.findOne(query, ProductOrder.class);
		
		Update update = new Update();
		update.set("status", ProductOrder.STATUS_REVIEW_PENDING);
		
		WriteResult result = mongoTemplate.updateFirst(query, update, ProductOrder.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		
		// create transaction and balance update
		Query transactionQuery = new Query();
		Criteria transactionCriteria = Criteria.where("id").is(orderId);
		transactionQuery.addCriteria(transactionCriteria);
		ProductOrder order = mongoTemplate.findOne(transactionQuery, ProductOrder.class);
		if (null != order) {
			Transaction transaction = new Transaction();
			transaction.setUserId(order.getSellerId());
			transaction.setTitle(order.getTitle());
			transaction.setCreationDate(LocalDateTime.now());
			transaction.setUnitPrice(order.getUnitPrice());
			transaction.setPriceTotal(Float.valueOf(order.getUnitPrice().floatValue()*order.getVolume().intValue()));
			transaction.setVolume(order.getVolume());
			transaction.setOrderNumber(order.getOrderNumber());
			if (null != order.getAlipayOrderNumber()) {
				transaction.setAlipayOrderNumber(order.getAlipayOrderNumber());
			}
			transaction.setPhotoId(order.getProductPhotoId());
			transaction.setOrderCreationDate(order.getCreationDate());
			transaction.setOrderPaidDate(order.getPaidDate());
			transaction.setStatus(Transaction.STATUS_COMPLETED_ORDER);
			mongoTemplate.save(transaction);
			Query balanceQuery = new Query();
			Criteria balanceCriteria = Criteria.where("phoneNumber").is(order.getSellerId());
			balanceQuery.addCriteria(balanceCriteria);
			Update balanceUpdate = new Update();
			balanceUpdate.inc("balance", Float.valueOf(order.getUnitPrice().floatValue()*order.getVolume().intValue()));
			WriteResult balanceResult = mongoTemplate.updateFirst(balanceQuery, balanceUpdate, User.class);
			if (!balanceResult.isUpdateOfExisting()) {
				logger.error("Cannot update balance, user:" + order.getSellerId() + " balance:" + Integer.valueOf(order.getUnitPrice().intValue()*order.getVolume().intValue()));
			}
		} else {
			logger.error("buyerReceivedProductOrder - cannot locate order, transaction ignored");
		}
		
		User user = identityDataManager.findUser(productOrder.getSellerPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链订单");
							unicast.setTitle("您的货物订单已确认收货");
							unicast.setText("您的货物订单已确认收货，请及时评价订单。");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("您的货物订单已确认收货");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}
		
		return true;
	}
	
	public boolean writeProductOrderReview(String phoneNumber, WriteProductOrderReviewRequest req) {
		boolean processed = false;
		
		// update buyer review as necessary
		Query buyerQuery = new Query();
		Criteria buyerIdCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria buyerUserCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria buyerStatusCriteria = Criteria.where("status").is(ProductOrder.STATUS_REVIEW_PENDING);
		Criteria buyerAndCriteria = new Criteria();
		buyerAndCriteria.andOperator(buyerIdCriteria, buyerUserCriteria, buyerStatusCriteria);
		buyerQuery.addCriteria(buyerAndCriteria);
		Update buyerUpdate = new Update();
		buyerUpdate.set("buyerStarCount", req.getStarCount());
		buyerUpdate.set("buyerReview", req.getReviewContent());
		WriteResult buyerResult = mongoTemplate.updateFirst(buyerQuery, buyerUpdate, ProductOrder.class);
		if (buyerResult.isUpdateOfExisting()) {
			processed = true;
		}
		
		// update seller review as necessary
		Query sellerQuery = new Query();
		Criteria sellerIdCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria sellerUserCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria sellerStatusCriteria = Criteria.where("status").is(ProductOrder.STATUS_REVIEW_PENDING);
		Criteria sellerAndCriteria = new Criteria();
		sellerAndCriteria.andOperator(sellerIdCriteria, sellerUserCriteria, sellerStatusCriteria);
		sellerQuery.addCriteria(sellerAndCriteria);
		Update sellerUpdate = new Update();
		sellerUpdate.set("sellerStarCount", req.getStarCount());
		sellerUpdate.set("sellerReview", req.getReviewContent());
		WriteResult sellerResult = mongoTemplate.updateFirst(sellerQuery, sellerUpdate, ProductOrder.class);
		if (sellerResult.isUpdateOfExisting()) {
			processed = true;
		}
		
		if (!processed) {
			return false;
		}
		
		// create review object as necessary
		Query reviewQuery = new Query();
		Criteria reviewCriteria = Criteria.where("id").is(req.getOrderId());
		reviewQuery.addCriteria(reviewCriteria);
		ProductOrder order = mongoTemplate.findOne(reviewQuery, ProductOrder.class);
		if (0 == order.getBuyerId().compareTo(phoneNumber)) {
			Review review = new Review();
			review.setUserId(order.getSellerId());
			review.setBuyerPhoneNumber(order.getBuyerId());
			review.setOrderId(order.getId());
			review.setStarCount(order.getBuyerStarCount());
			review.setReviewContent(order.getBuyerReview());
			review.setProductTitle(order.getTitle());
			review.setProductOrigin(order.getOrigin());
			review.setPhotoId(order.getProductPhotoId());
			review.setCreationDate(new Date());
			mongoTemplate.save(review);
		}
		
		// update order status
		Query checkQuery = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria buyerReviewCriteria = Criteria.where("buyerReview").exists(true);
		Criteria sellerReviewCriteria = Criteria.where("sellerReview").exists(true);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, buyerReviewCriteria, sellerReviewCriteria);
		checkQuery.addCriteria(andCriteria);
		Update checkUpdate = new Update();
		checkUpdate.set("status", ProductOrder.STATUS_COMPLETED);
		WriteResult reviewResult = mongoTemplate.updateFirst(checkQuery, checkUpdate, ProductOrder.class);
		if (reviewResult.isUpdateOfExisting()) {
			// order completed
			Query balanceQuery = new Query();
			Criteria balanceCriteria = Criteria.where("phoneNumber").is(order.getSellerId());
			balanceQuery.addCriteria(balanceCriteria);
			Update balanceUpdate = new Update();
			balanceUpdate.inc("withdrawBalance", Float.valueOf(order.getUnitPrice().floatValue()*order.getVolume().intValue()));
			WriteResult balanceResult = mongoTemplate.updateFirst(balanceQuery, balanceUpdate, User.class);
			if (!balanceResult.isUpdateOfExisting()) {
				logger.error("Cannot update balance, user:" + order.getSellerId() + " balance:" + Integer.valueOf(order.getUnitPrice().intValue()*order.getVolume().intValue()));
			}
		}
		
		return true;
	}
	
	public boolean writeWarehouseOrderReview(String phoneNumber, WriteWarehouseOrderReviewRequest req) {
		boolean processed = false;
		
		// update buyer review as necessary
		Query buyerQuery = new Query();
		Criteria buyerIdCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria buyerUserCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria buyerStatusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_REVIEW_PENDING);
		Criteria buyerAndCriteria = new Criteria();
		buyerAndCriteria.andOperator(buyerIdCriteria, buyerUserCriteria, buyerStatusCriteria);
		buyerQuery.addCriteria(buyerAndCriteria);
		Update buyerUpdate = new Update();
		buyerUpdate.set("buyerStarCount", req.getStarCount());
		buyerUpdate.set("buyerReview", req.getReviewContent());
		WriteResult buyerResult = mongoTemplate.updateFirst(buyerQuery, buyerUpdate, WarehouseOrder.class);
		if (buyerResult.isUpdateOfExisting()) {
			processed = true;
		}
		
		// update seller review as necessary
		Query sellerQuery = new Query();
		Criteria sellerIdCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria sellerUserCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria sellerStatusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_REVIEW_PENDING);
		Criteria sellerAndCriteria = new Criteria();
		sellerAndCriteria.andOperator(sellerIdCriteria, sellerUserCriteria, sellerStatusCriteria);
		sellerQuery.addCriteria(sellerAndCriteria);
		Update sellerUpdate = new Update();
		sellerUpdate.set("sellerStarCount", req.getStarCount());
		sellerUpdate.set("sellerReview", req.getReviewContent());
		WriteResult sellerResult = mongoTemplate.updateFirst(sellerQuery, sellerUpdate, WarehouseOrder.class);
		if (sellerResult.isUpdateOfExisting()) {
			processed = true;
		}
		
		if (!processed) {
			return false;
		}
		
		// create review object as necessary
		Query reviewQuery = new Query();
		Criteria reviewCriteria = Criteria.where("id").is(req.getOrderId());
		reviewQuery.addCriteria(reviewCriteria);
		WarehouseOrder order = mongoTemplate.findOne(reviewQuery, WarehouseOrder.class);
		if (0 == order.getBuyerId().compareTo(phoneNumber)) {
			Review review = new Review();
			review.setUserId(order.getSellerId());
			review.setBuyerPhoneNumber(order.getBuyerId());
			review.setOrderId(order.getId());
			review.setStarCount(order.getBuyerStarCount());
			review.setReviewContent(order.getBuyerReview());
			review.setWarehouseName(order.getWarehouseName());
			review.setWarehouseAddress(order.getWarehouseAddress());
			review.setPhotoId(order.getWarehousePhotoId());
			review.setCreationDate(new Date());
			mongoTemplate.save(review);
		}
		
		// update order status
		Query checkQuery = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria buyerReviewCriteria = Criteria.where("buyerReview").exists(true);
		Criteria sellerReviewCriteria = Criteria.where("sellerReview").exists(true);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, buyerReviewCriteria, sellerReviewCriteria);
		checkQuery.addCriteria(andCriteria);
		Update checkUpdate = new Update();
		checkUpdate.set("status", WarehouseOrder.STATUS_COMPLETED);
		WriteResult reviewResult = mongoTemplate.updateFirst(checkQuery, checkUpdate, WarehouseOrder.class);
		if (reviewResult.isUpdateOfExisting()) {
			Query balanceQuery = new Query();
			Criteria balanceCriteria = Criteria.where("phoneNumber").is(order.getSellerId());
			balanceQuery.addCriteria(balanceCriteria);
			Update balanceUpdate = new Update();
			int days = DateUtil.daysBetween(order.getStartDate(), order.getUntilDate());
			balanceUpdate.inc("withdrawBalance", Float.valueOf(order.getUnitPrice().floatValue()*order.getVolume().intValue()*days));
			WriteResult balanceResult = mongoTemplate.updateFirst(balanceQuery, balanceUpdate, User.class);
			if (!balanceResult.isUpdateOfExisting()) {
				logger.error("Cannot update balance, user:" + order.getSellerId() + " balance:" + Integer.valueOf(order.getUnitPrice().intValue()*order.getVolume().intValue()*days));
			}
		}
		
		return true;
	}
	
	public boolean writeVehicleOrderReview(String phoneNumber, WriteVehicleOrderReviewRequest req) {
		boolean processed = false;
		
		// update buyer review as necessary
		Query buyerQuery = new Query();
		Criteria buyerIdCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria buyerUserCriteria = Criteria.where("buyerId").is(phoneNumber);
		Criteria buyerStatusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_REVIEW_PENDING);
		Criteria buyerAndCriteria = new Criteria();
		buyerAndCriteria.andOperator(buyerIdCriteria, buyerUserCriteria, buyerStatusCriteria);
		buyerQuery.addCriteria(buyerAndCriteria);
		Update buyerUpdate = new Update();
		buyerUpdate.set("buyerStarCount", req.getStarCount());
		buyerUpdate.set("buyerReview", req.getReviewContent());
		WriteResult buyerResult = mongoTemplate.updateFirst(buyerQuery, buyerUpdate, CompleteVehicleOrder.class);
		if (buyerResult.isUpdateOfExisting()) {
			processed = true;
		}
		
		// update seller review as necessary
		Query sellerQuery = new Query();
		Criteria sellerIdCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria sellerUserCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria sellerStatusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_REVIEW_PENDING);
		Criteria sellerAndCriteria = new Criteria();
		sellerAndCriteria.andOperator(sellerIdCriteria, sellerUserCriteria, sellerStatusCriteria);
		sellerQuery.addCriteria(sellerAndCriteria);
		Update sellerUpdate = new Update();
		sellerUpdate.set("sellerStarCount", req.getStarCount());
		sellerUpdate.set("sellerReview", req.getReviewContent());
		WriteResult sellerResult = mongoTemplate.updateFirst(sellerQuery, sellerUpdate, CompleteVehicleOrder.class);
		if (sellerResult.isUpdateOfExisting()) {
			processed = true;
		}
		
		if (!processed) {
			return false;
		}
		
		// create review object as necessary
		Query reviewQuery = new Query();
		Criteria reviewCriteria = Criteria.where("id").is(req.getOrderId());
		reviewQuery.addCriteria(reviewCriteria);
		CompleteVehicleOrder order = mongoTemplate.findOne(reviewQuery, CompleteVehicleOrder.class);
		if (0 == order.getBuyerId().compareTo(phoneNumber)) {
			Review review = new Review();
			review.setUserId(order.getSellerId());
			review.setBuyerPhoneNumber(order.getBuyerId());
			review.setOrderId(order.getId());
			review.setStarCount(order.getBuyerStarCount());
			review.setReviewContent(order.getBuyerReview());
			review.setLicensePlate(order.getVehicle().get("licensePlate").toString());
			review.setRouteOrigin(order.getRouteOrigin());
			review.setRouteDestination(order.getRouteDestination());
			review.setPhotoId(order.getVehicle().get("vehiclePhotoId").toString());
			review.setCreationDate(new Date());
			mongoTemplate.save(review);
		}
		
		// update order status
		Query checkQuery = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria buyerReviewCriteria = Criteria.where("buyerReview").exists(true);
		Criteria sellerReviewCriteria = Criteria.where("sellerReview").exists(true);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, buyerReviewCriteria, sellerReviewCriteria);
		checkQuery.addCriteria(andCriteria);
		Update checkUpdate = new Update();
		checkUpdate.set("status", CompleteVehicleOrder.STATUS_COMPLETED);
		WriteResult reviewResult = mongoTemplate.updateFirst(checkQuery, checkUpdate, CompleteVehicleOrder.class);
		if (reviewResult.isUpdateOfExisting()) {
			Query balanceQuery = new Query();
			Criteria balanceCriteria = Criteria.where("phoneNumber").is(order.getSellerId());
			balanceQuery.addCriteria(balanceCriteria);
			Update balanceUpdate = new Update();
			balanceUpdate.inc("withdrawBalance", order.getPrice());
			WriteResult balanceResult = mongoTemplate.updateFirst(balanceQuery, balanceUpdate, User.class);
			if (!balanceResult.isUpdateOfExisting()) {
				logger.error("Cannot update balance, user:" + order.getSellerId() + " balance:" + order.getPrice());
			}
		}
		
		return true;
	}
	
	public boolean sellerAssignVehicleToVehicleOrder(String phoneNumber, SellerAssignVehicleToVehicleOrderRequest req) {
		// locate vehicle
		Query vehicleQuery = new Query();
		Criteria vehicleIdCriteria = Criteria.where("id").is(req.getVehicleId());
		Criteria vehicleUserCriteria = Criteria.where("userId").is(phoneNumber);
		Criteria vehicleAndCriteria = new Criteria();
		vehicleAndCriteria.andOperator(vehicleIdCriteria, vehicleUserCriteria);
		vehicleQuery.addCriteria(vehicleAndCriteria);
		Vehicle vehicle = mongoTemplate.findOne(vehicleQuery, Vehicle.class);
		if (null == vehicle) {
			return false;
		}
		
		// locate driver
		Query driverQuery = new Query();
		Criteria driverIdCriteria = Criteria.where("id").is(req.getDriverId());
		Criteria driverUserCriteria = Criteria.where("userId").is(phoneNumber);
		Criteria driverAndCriteria = new Criteria();
		driverAndCriteria.andOperator(driverIdCriteria, driverUserCriteria);
		driverQuery.addCriteria(driverAndCriteria);
		Driver driver = mongoTemplate.findOne(driverQuery, Driver.class);
		if (null == driver) {
			return false;
		}
		
		// update order
		Query orderQuery = new Query();
		Criteria orderIdCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria orderUserCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria orderStatusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_TRANSPORT_PENDING);
		Criteria orderAndCriteria = new Criteria();
		orderAndCriteria.andOperator(orderIdCriteria, orderUserCriteria, orderStatusCriteria);
		orderQuery.addCriteria(orderAndCriteria);
		Update orderUpdate = new Update();
		orderUpdate.set("driver", driver.toMap());
		orderUpdate.set("vehicle", vehicle.toMap());
		WriteResult result = mongoTemplate.updateFirst(orderQuery, orderUpdate, CompleteVehicleOrder.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		
		return true;
	}
	
	public boolean sellerTransportsVehicleOrder(String phoneNumber, String orderId) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(orderId);
		Criteria userCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_TRANSPORT_PENDING);
		Criteria vehicleCriteria = Criteria.where("vehicle").exists(true);
		Criteria driverCriteria = Criteria.where("driver").exists(true);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, userCriteria, statusCriteria, vehicleCriteria, driverCriteria);
		query.addCriteria(andCriteria);
		CompleteVehicleOrder vehicleOrder = mongoTemplate.findOne(query, CompleteVehicleOrder.class);
		
		Update update = new Update();
		update.set("status", CompleteVehicleOrder.STATUS_TRANSPORT_ONGOING);
		
		WriteResult result = mongoTemplate.updateFirst(query, update, CompleteVehicleOrder.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		
		// create device order as necessary
		if (null != vehicleOrder.getVehicle().get("deviceSN")) {
			Query temperatureQuery = new Query();
			Criteria temperatureCriteria = Criteria.where("identifier").is(vehicleOrder.getTemperatureSpec());
			temperatureQuery.addCriteria(temperatureCriteria);
			TransportationTemperature temperature = mongoTemplate.findOne(temperatureQuery, TransportationTemperature.class);
			if (null != temperature) {
				DeviceOrder deviceOrder = new DeviceOrder();
				deviceOrder.setDeviceSN(vehicleOrder.getDeviceSN());
				deviceOrder.setType(DeviceOrder.TYPE_VEHICLE);
				deviceOrder.setOrderId(vehicleOrder.getId());
				List<String> viewers = new ArrayList<String>();
				viewers.add(vehicleOrder.getBuyerId());
				viewers.add(vehicleOrder.getSellerId());
				deviceOrder.setViewers(viewers);
				deviceOrder.setTemperatureMin(temperature.getTemperatureMin());
				deviceOrder.setTemperatureMax(temperature.getTemperatureMax());
				deviceOrder.setLicensePlate((String)vehicleOrder.getVehicle().get("licensePlate"));
				redisTemplate.opsForValue().set((String)vehicleOrder.getVehicle().get("deviceSN"), deviceOrder);
			} else {
				logger.warn("sellerTransportsVehicleOrder - cannot locate temperature definition with identifier: " + vehicleOrder.getTemperatureSpec());
			}
		} else {
			logger.debug("no device has installed on vehicle");
		}
		
		User user = identityDataManager.findUser(vehicleOrder.getBuyerPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链订单");
							unicast.setTitle("您的车辆订单已发车");
							unicast.setText("恭喜您的车辆订单已发车，请及时确认。");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("您的车辆订单已发车");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}
		
		return true;
	}
	
	public boolean sellerLoadedWarehouseOrder(String phoneNumber, String orderId) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(orderId);
		Criteria userCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_LOAD_PENDING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
		query.addCriteria(andCriteria);
		WarehouseOrder warehouseOrder = mongoTemplate.findOne(query, WarehouseOrder.class);
		
		Update update = new Update();
		update.set("status", WarehouseOrder.STATUS_LOADED);
		
		WriteResult result = mongoTemplate.updateFirst(query, update, WarehouseOrder.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		User user = identityDataManager.findUser(warehouseOrder.getBuyerPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链订单");
							unicast.setTitle("您的冷库订单已入库");
							unicast.setText("恭喜您的冷库订单已入库，如需延长用库时间请及时预定。");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("您的冷库订单已入库");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}
		
		return true;
	}
	
	public boolean sellerOffloadWarehouseOrder(String phoneNumber, String orderId) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(orderId);
		Criteria userCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_LOADED);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
		query.addCriteria(andCriteria);
		WarehouseOrder warehouseOrder = mongoTemplate.findOne(query, WarehouseOrder.class);
		
		Update update = new Update();
		update.set("status", WarehouseOrder.STATUS_REVIEW_PENDING);
		
		WriteResult result = mongoTemplate.updateFirst(query, update, WarehouseOrder.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		
		// create transaction and balance update
		Query transactionQuery = new Query();
		Criteria transactionCriteria = Criteria.where("id").is(orderId);
		transactionQuery.addCriteria(transactionCriteria);
		WarehouseOrder order = mongoTemplate.findOne(transactionQuery, WarehouseOrder.class);
		if (null != order) {
			Transaction transaction = new Transaction();
			transaction.setUserId(order.getSellerId());
			transaction.setTitle(order.getWarehouseName());
			transaction.setCreationDate(LocalDateTime.now());
			transaction.setUnitPrice(order.getUnitPrice());
			transaction.setPriceTotal(Float.valueOf(order.getUnitPrice().floatValue()*order.getVolume().intValue()));
			transaction.setVolume(order.getVolume());
			transaction.setOrderNumber(order.getOrderNumber());
			if (null != order.getAlipayOrderNumber()) {
				transaction.setAlipayOrderNumber(order.getAlipayOrderNumber());
			}
			transaction.setPhotoId(order.getWarehousePhotoId());
			transaction.setOrderCreationDate(order.getCreationDate());
			transaction.setOrderPaidDate(order.getPaidDate());
			transaction.setStatus(Transaction.STATUS_COMPLETED_ORDER);
			mongoTemplate.save(transaction);
			Query balanceQuery = new Query();
			Criteria balanceCriteria = Criteria.where("phoneNumber").is(order.getSellerId());
			balanceQuery.addCriteria(balanceCriteria);
			Update balanceUpdate = new Update();
			int days = DateUtil.daysBetween(order.getStartDate(), order.getUntilDate());
			balanceUpdate.inc("balance", Float.valueOf(order.getUnitPrice().floatValue()*order.getVolume().intValue()*days));
			WriteResult balanceResult = mongoTemplate.updateFirst(balanceQuery, balanceUpdate, User.class);
			if (!balanceResult.isUpdateOfExisting()) {
				logger.error("Cannot update balance, user:" + order.getSellerId() + " balance:" + Integer.valueOf(order.getUnitPrice().intValue()*order.getVolume().intValue()*days));
			}
		} else {
			logger.error("buyerReceivedProductOrder - cannot locate order, transaction ignored");
		}
		User user = identityDataManager.findUser(warehouseOrder.getBuyerPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链订单");
							unicast.setTitle("您的冷库订单已出库");
							unicast.setText("您的冷库订单已出库，请及时做出评价。");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("您的冷库订单已出库");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}
		
		return true;
	}
	
	public boolean sellerTransportedVehicleOrder(String phoneNumber, String orderId) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(orderId);
		Criteria userCriteria = Criteria.where("sellerId").is(phoneNumber);
		Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_TRANSPORT_ONGOING);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
		query.addCriteria(andCriteria);
		CompleteVehicleOrder vehicleOrder = mongoTemplate.findOne(query, CompleteVehicleOrder.class);
		
		Update update = new Update();
		update.set("status", CompleteVehicleOrder.STATUS_REVIEW_PENDING);
		
		WriteResult result = mongoTemplate.updateFirst(query, update, CompleteVehicleOrder.class);
		if (!result.isUpdateOfExisting()) {
			return false;
		}
		
		// create transaction and balance update, unlink device order as necessary
		Query transactionQuery = new Query();
		Criteria transactionCriteria = Criteria.where("id").is(orderId);
		transactionQuery.addCriteria(transactionCriteria);
		CompleteVehicleOrder order = mongoTemplate.findOne(transactionQuery, CompleteVehicleOrder.class);
		if (null != order) {
			if (null != order.getVehicle().get("deviceSN")) {
				redisTemplate.delete((String)order.getVehicle().get("deviceSN"));
				Date now = new Date();
				Query locationQuery = new Query();
				Criteria locationIdCriteria = Criteria.where("orderId").is(orderId);
				Criteria locationUserCriteria = Criteria.where("viewers").all(phoneNumber);
				Criteria locationDateCriteria = Criteria.where("recordingStart").exists(true);
				Criteria locationAndCriteria = new Criteria();
				locationAndCriteria.andOperator(locationIdCriteria, locationUserCriteria, locationDateCriteria);
				locationQuery.addCriteria(locationAndCriteria);
				Update locationUpdate = new Update();
				locationUpdate.set("recordingEnd", now);
				if (!mongoTemplate.updateFirst(locationQuery, locationUpdate, LocationDocument.class).isUpdateOfExisting()) {
					logger.error("failed to update location's recordingEnd for order: " + orderId);
				}
				Query temperatureQuery = new Query();
				Criteria temperatureIdCriteria = Criteria.where("orderId").is(orderId);
				Criteria temperatureUserCriteria = Criteria.where("viewers").all(phoneNumber);
				Criteria temperatureDateCriteria = Criteria.where("recordingStart").exists(true);
				Criteria temperatureAndCriteria = new Criteria();
				temperatureAndCriteria.andOperator(temperatureIdCriteria, temperatureUserCriteria, temperatureDateCriteria);
				temperatureQuery.addCriteria(temperatureAndCriteria);
				Update temperatureUpdate = new Update();
				temperatureUpdate.set("recordingEnd", now);
				if (!mongoTemplate.updateFirst(temperatureQuery, temperatureUpdate, TemperatureDocument.class).isUpdateOfExisting()) {
					logger.error("failed to update temperature's recordingEnd for order: " + orderId);
				}
				Query humidityQuery = new Query();
				Criteria humidityIdCriteria = Criteria.where("orderId").is(orderId);
				Criteria humidityUserCriteria = Criteria.where("viewers").all(phoneNumber);
				Criteria humidityDateCriteria = Criteria.where("recordingStart").exists(true);
				Criteria humidityAndCriteria = new Criteria();
				humidityAndCriteria.andOperator(humidityIdCriteria, humidityUserCriteria, humidityDateCriteria);
				humidityQuery.addCriteria(humidityAndCriteria);
				Update humidityUpdate = new Update();
				humidityUpdate.set("recordingEnd", now);
				if (!mongoTemplate.updateFirst(humidityQuery, humidityUpdate, HumidityDocument.class).isUpdateOfExisting()) {
					logger.error("failed to update humidity's recordingEnd for order: " + orderId);
				}
			}
			
			Transaction transaction = new Transaction();
			transaction.setUserId(order.getSellerId());
			transaction.setTitle(order.getVehicle().get("licensePlate").toString());
			transaction.setCreationDate(LocalDateTime.now());
			transaction.setUnitPrice(order.getPrice());
			transaction.setPriceTotal(order.getPrice());
			transaction.setVolume(Integer.valueOf(1));
			transaction.setOrderNumber(order.getOrderNumber());
			if (null != order.getAlipayOrderNumber()) {
				transaction.setAlipayOrderNumber(order.getAlipayOrderNumber());
			}
			transaction.setPhotoId(order.getVehicle().get("vehiclePhotoId").toString());
			transaction.setOrderCreationDate(order.getCreationDate());
			transaction.setOrderPaidDate(order.getPaidDate());
			transaction.setStatus(Transaction.STATUS_COMPLETED_ORDER);
			mongoTemplate.save(transaction);
			Query balanceQuery = new Query();
			Criteria balanceCriteria = Criteria.where("phoneNumber").is(order.getSellerId());
			balanceQuery.addCriteria(balanceCriteria);
			Update balanceUpdate = new Update();
			balanceUpdate.inc("balance", order.getPrice());
			WriteResult balanceResult = mongoTemplate.updateFirst(balanceQuery, balanceUpdate, User.class);
			if (!balanceResult.isUpdateOfExisting()) {
				logger.error("Cannot update balance, user:" + order.getSellerId() + " balance:" + order.getPrice());
			}
		} else {
			logger.error("buyerReceivedProductOrder - cannot locate order, transaction ignored");
		}
		
		User user = identityDataManager.findUser(vehicleOrder.getBuyerPhoneNumber());
		if (null != user) {
			String userDevice = user.getMobileDeviceOS();
			if (null != userDevice) {
				// signed in from mobile device
				if (0 == userDevice.compareTo("Android")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							AndroidUnicast unicast = new AndroidUnicast("56e043af67e58ecf42000683", "zv4puvb3xuc3ebzfaq4qqebcfdgg1iml");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setTicker("中国冷链订单");
							unicast.setTitle("您的车辆订单已完成运输");
							unicast.setText("恭喜您的车辆订单已完成运输，请及时确认。");
							unicast.goAppAfterOpen();
							unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
							unicast.setProductionMode();
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post android notification for user - " + user.getPhoneNumber());
						}
					}
				} else if (0 == userDevice.compareTo("iOS")) {
					if (null == user.getDeviceToken()) {
						logger.warn("device token not updated for user - " + user.getPhoneNumber());
					} else {
						try {
							PushClient client = new PushClient();
							IOSUnicast unicast = new IOSUnicast("57565bd5e0f55a9bc200149d", "dzzxjb8fxp549b4rq6cktekirpvxnoe3");
							unicast.setDeviceToken(user.getDeviceToken());
							unicast.setAlert("您的车辆订单已完成运输");
							unicast.setBadge(0);
							unicast.setSound("default");
							if (MessageController.isRunningOnProduction) {
								unicast.setProductionMode();
							} else {
								unicast.setTestMode();
							}
							client.send(unicast);
						} catch (Exception e) {
							logger.warn("failed to post iOS notification for user - " + user.getPhoneNumber());
						}
					}
				}
			}
		}

		return true;
	}
	
	public List<Map<String, Object>> transactionListing(String phoneNumber) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		
		Query query = new Query();
		Criteria userCriteria = Criteria.where("userId").is(phoneNumber);
		query.addCriteria(userCriteria);
		
		List<Transaction> list = mongoTemplate.find(query, Transaction.class);
		for (Transaction transaction : list) {
			result.add(transaction.toMap());
		}
		
		return result;
	}
	
	public List<Map<String, Object>> sellerReviewListing(SellerReviewListingRequest req) {
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		String sellerPhoneNumber = null;
		if (null != req.getProductId()) {
			Query locateQuery = new Query();
			Criteria locateCriteria = Criteria.where("id").is(req.getProductId());
			locateQuery.addCriteria(locateCriteria);
			ProductService product = mongoTemplate.findOne(locateQuery, ProductService.class);
			if (null != product) {
				sellerPhoneNumber = product.getUserId();
			}
		} else {
			if (null != req.getWarehouseId()) {
				Query locateQuery = new Query();
				Criteria locateCriteria = Criteria.where("id").is(req.getWarehouseId());
				locateQuery.addCriteria(locateCriteria);
				WarehouseService warehouse = mongoTemplate.findOne(locateQuery, WarehouseService.class);
				if (null != warehouse) {
					sellerPhoneNumber = warehouse.getUserId();
				}
			} else {
				if (null != req.getVehicleId()) {
					Query locateQuery = new Query();
					Criteria locateCriteria = Criteria.where("id").is(req.getVehicleId());
					locateQuery.addCriteria(locateCriteria);
					VehicleService vehicle = mongoTemplate.findOne(locateQuery, VehicleService.class);
					if (null != vehicle) {
						sellerPhoneNumber = vehicle.getUserId();
					}
				}
			}
		}
		
		if (null == sellerPhoneNumber) {
			logger.warn("sellerReviewListing - cannot locate service object");
			return result;
		}
		
		Query query = new Query();
		Criteria userCriteria = Criteria.where("userId").is(sellerPhoneNumber);
		query.addCriteria(userCriteria);
		query.with(new PageRequest(0, 30));
		List<Review> list = mongoTemplate.find(query, Review.class);
		for (Review review : list) {
			result.add(review.toMap());
		}
		
		return result;
	}
	
	public boolean submitAppealRequest(String phoneNumber, SubmitAppealRequestRequest req) {
		if (0 == req.getLocator().compareTo("vehicle")) {
			Query query = new Query();
			Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
			Criteria userCriteria = Criteria.where("buyerId").is(phoneNumber);
			Criteria statusCriteria = Criteria.where("status").is(CompleteVehicleOrder.STATUS_REVIEW_PENDING);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
			query.addCriteria(andCriteria);
			CompleteVehicleOrder vehicleOrder = mongoTemplate.findOne(query, CompleteVehicleOrder.class);
			if (null == vehicleOrder) {
				logger.warn("submitAppealRequest - cannot locate vehicle order, id: " + req.getOrderId());
				return false;
			}
			Update update = new Update();
			update.set("status", CompleteVehicleOrder.STATUS_APPEAL_ONGOING);
			update.set("appealSubmissionDate", new Date());
			update.set("refundAmount", req.getRefundAmount());
			update.set("appealDescription", req.getDescription());
			WriteResult result = mongoTemplate.updateFirst(query, update, CompleteVehicleOrder.class);
			if (!result.isUpdateOfExisting()) {
				return false;
			}
			// reduce seller balance
			Query balanceQuery = new Query();
			Criteria balanceCriteria = Criteria.where("phoneNumber").is(vehicleOrder.getSellerId());
			balanceQuery.addCriteria(balanceCriteria);
			Update balanceUpdate = new Update();
			balanceUpdate.inc("balance", Integer.valueOf(vehicleOrder.getPrice().intValue()*-1));
			WriteResult balanceResult = mongoTemplate.updateFirst(balanceQuery, balanceUpdate, User.class);
			if (!balanceResult.isUpdateOfExisting()) {
				logger.error("Cannot update balance, user:" + vehicleOrder.getSellerId() + " balance:" + vehicleOrder.getPrice());
			}
			// compose message
			Message buyerMessage = new Message();
			buyerMessage.setId(UUID.randomUUID().toString());
			buyerMessage.setDate(new Date());
			buyerMessage.setTitle("您的订单申诉已提交");
			buyerMessage.setContent("您的订单（订单号：" + vehicleOrder.getAlipayOrderNumber() + "）申诉已提交，我们会尽快处理，请耐心等待");
			buyerMessage.setFlag(Message.FLAG_BLUE);
			buyerMessage.setSourceModule("OrderManagement");
			buyerMessage.setSourceEvent("submitAppealRequest");
			messageController.postMessage(buyerMessage, vehicleOrder.getBuyerId());
			Message sellerMessage = new Message();
			sellerMessage.setId(UUID.randomUUID().toString());
			sellerMessage.setDate(new Date());
			sellerMessage.setTitle("您收到订单申诉");
			sellerMessage.setContent("您收到订单（订单号：" + vehicleOrder.getAlipayOrderNumber() + "）申诉，原因：" + req.getDescription() + "。我们会尽快处理，请耐心等待");
			sellerMessage.setFlag(Message.FLAG_RED);
			sellerMessage.setSourceModule("OrderManagement");
			sellerMessage.setSourceEvent("submitAppealRequest");
			messageController.postMessage(sellerMessage, vehicleOrder.getSellerId());
			return true;
		} else if (0 == req.getLocator().compareTo("warehouse")) {
			Query query = new Query();
			Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
			Criteria userCriteria = Criteria.where("buyerId").is(phoneNumber);
			Criteria statusCriteria = Criteria.where("status").is(WarehouseOrder.STATUS_REVIEW_PENDING);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
			query.addCriteria(andCriteria);
			WarehouseOrder warehouseOrder = mongoTemplate.findOne(query, WarehouseOrder.class);
			if (null == warehouseOrder) {
				logger.warn("submitAppealRequest - cannot locate warehouse order, id: " + req.getOrderId());
				return false;
			}
			Update update = new Update();
			update.set("status", WarehouseOrder.STATUS_APPEAL_ONGOING);
			update.set("appealSubmissionDate", new Date());
			update.set("refundAmount", req.getRefundAmount());
			update.set("appealDescription", req.getDescription());
			WriteResult result = mongoTemplate.updateFirst(query, update, WarehouseOrder.class);
			if (!result.isUpdateOfExisting()) {
				return false;
			}
			// reduce seller balance
			Query balanceQuery = new Query();
			Criteria balanceCriteria = Criteria.where("phoneNumber").is(warehouseOrder.getSellerId());
			balanceQuery.addCriteria(balanceCriteria);
			Update balanceUpdate = new Update();
			int days = DateUtil.daysBetween(warehouseOrder.getStartDate(), warehouseOrder.getUntilDate());
			balanceUpdate.inc("balance", Integer.valueOf(warehouseOrder.getUnitPrice().intValue()*warehouseOrder.getVolume().intValue()*days*-1));
			WriteResult balanceResult = mongoTemplate.updateFirst(balanceQuery, balanceUpdate, User.class);
			if (!balanceResult.isUpdateOfExisting()) {
				logger.error("Cannot update balance, user:" + warehouseOrder.getSellerId() + " balance:" + Integer.valueOf(warehouseOrder.getUnitPrice().intValue()*warehouseOrder.getVolume().intValue()*days*-1));
			}
			// compose message
			Message buyerMessage = new Message();
			buyerMessage.setId(UUID.randomUUID().toString());
			buyerMessage.setDate(new Date());
			buyerMessage.setTitle("您的订单申诉已提交");
			buyerMessage.setContent("您的订单（订单号：" + warehouseOrder.getAlipayOrderNumber() + "）申诉已提交，我们会尽快处理，请耐心等待");
			buyerMessage.setFlag(Message.FLAG_BLUE);
			buyerMessage.setSourceModule("OrderManagement");
			buyerMessage.setSourceEvent("submitAppealRequest");
			messageController.postMessage(buyerMessage, warehouseOrder.getBuyerId());
			Message sellerMessage = new Message();
			sellerMessage.setId(UUID.randomUUID().toString());
			sellerMessage.setDate(new Date());
			sellerMessage.setTitle("您收到订单申诉");
			sellerMessage.setContent("您收到订单（订单号：" + warehouseOrder.getAlipayOrderNumber() + "）申诉，原因：" + req.getDescription() + "。我们会尽快处理，请耐心等待");
			sellerMessage.setFlag(Message.FLAG_RED);
			sellerMessage.setSourceModule("OrderManagement");
			sellerMessage.setSourceEvent("submitAppealRequest");
			messageController.postMessage(sellerMessage, warehouseOrder.getSellerId());
			return true;
		} else if (0 == req.getLocator().compareTo("product")) {
			Query query = new Query();
			Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
			Criteria userCriteria = Criteria.where("buyerId").is(phoneNumber);
			Criteria statusCriteria = Criteria.where("status").is(ProductOrder.STATUS_SHIPPED);
			Criteria andCriteria = new Criteria();
			andCriteria.andOperator(idCriteria, userCriteria, statusCriteria);
			query.addCriteria(andCriteria);
			ProductOrder productOrder = mongoTemplate.findOne(query, ProductOrder.class);
			if (null == productOrder) {
				logger.warn("submitAppealRequest - cannot locate product order, id: " + req.getOrderId());
				return false;
			}
			Update update = new Update();
			update.set("status", ProductOrder.STATUS_APPEAL_ONGOING);
			update.set("appealSubmissionDate", new Date());
			update.set("refundAmount", req.getRefundAmount());
			update.set("appealDescription", req.getDescription());
			WriteResult result = mongoTemplate.updateFirst(query, update, ProductOrder.class);
			if (!result.isUpdateOfExisting()) {
				return false;
			}
			// compose message
			Message buyerMessage = new Message();
			buyerMessage.setId(UUID.randomUUID().toString());
			buyerMessage.setDate(new Date());
			buyerMessage.setTitle("您的订单申诉已提交");
			buyerMessage.setContent("您的订单（订单号：" + productOrder.getAlipayOrderNumber() + "）申诉已提交，我们会尽快处理，请耐心等待");
			buyerMessage.setFlag(Message.FLAG_BLUE);
			buyerMessage.setSourceModule("OrderManagement");
			buyerMessage.setSourceEvent("submitAppealRequest");
			messageController.postMessage(buyerMessage, productOrder.getBuyerId());
			Message sellerMessage = new Message();
			sellerMessage.setId(UUID.randomUUID().toString());
			sellerMessage.setDate(new Date());
			sellerMessage.setTitle("您收到订单申诉");
			sellerMessage.setContent("您收到订单（订单号：" + productOrder.getAlipayOrderNumber() + "）申诉，原因：" + req.getDescription() + "。我们会尽快处理，请耐心等待");
			sellerMessage.setFlag(Message.FLAG_RED);
			sellerMessage.setSourceModule("OrderManagement");
			sellerMessage.setSourceEvent("submitAppealRequest");
			messageController.postMessage(sellerMessage, productOrder.getSellerId());
			return true;
		} else {
			logger.warn("submitAppealRequest - unrecognized locator: " + req.getLocator());
			return false;
		}
	}
	
	public Map<String, Object> orderInfo(String phoneNumber, OrderInfoRequest req) {
		Query query = new Query();
		Criteria idCriteria = Criteria.where("id").is(req.getOrderId());
		Criteria userCriteria = Criteria.where("userId").is(phoneNumber);
		Criteria andCriteria = new Criteria();
		andCriteria.andOperator(idCriteria, userCriteria);
		query.addCriteria(andCriteria);
		
		UserOrder order = mongoTemplate.findOne(query, UserOrder.class);
		if (null == order) {
			return null;
		}
		
		return order.toMap();
	}
	
	public boolean markOrderAsPaid(String orderId) {
		Query query = new Query();
		Criteria locateCriteria = Criteria.where("id").is(orderId);
		query.addCriteria(locateCriteria);
		
		//check vehicle order
		CompleteVehicleOrder vehicleOrder = mongoTemplate.findOne(query, CompleteVehicleOrder.class);
		if (null != vehicleOrder) {
			Update update = new Update();
			update.set("status", CompleteVehicleOrder.STATUS_TRANSPORT_PENDING);
			update.set("paidDate", new Date());
			WriteResult result = mongoTemplate.updateFirst(query, update, CompleteVehicleOrder.class);
			if (!result.isUpdateOfExisting()) {
				return false;
			} else {
				return true;
			}
		}
		
		// check warehouse order
		WarehouseOrder warehouseOrder = mongoTemplate.findOne(query, WarehouseOrder.class);
		if (null != warehouseOrder) {
			Update update = new Update();
			update.set("status", WarehouseOrder.STATUS_LOAD_PENDING);
			update.set("paidDate", new Date());
			WriteResult result = mongoTemplate.updateFirst(query, update, WarehouseOrder.class);
			if (!result.isUpdateOfExisting()) {
				return false;
			} else {
				return true;
			}
		}
		
		// check product order
		ProductOrder productOrder = mongoTemplate.findOne(query, ProductOrder.class);
		if (null != productOrder) {
			Update update = new Update();
			update.set("status", ProductOrder.STATUS_SHIP_PENDING);
			update.set("paidDate", new Date());
			WriteResult result = mongoTemplate.updateFirst(query, update, ProductOrder.class);
			if (!result.isUpdateOfExisting()) {
				return false;
			} else {
				return true;
			}
		}
		
		return false;
	}

	@Autowired
	private MongoTemplate mongoTemplate;
	
	@Autowired
	private ProductDataManager productDataManager;
	
	@Autowired
	private IdentityDataManager identityDataManager;
	
	@Autowired
	private CartDataManager cartDataManager;
	
	@Autowired
	private MessageController messageController;
	
	@Autowired
	private RedisTemplate<String, Object> redisTemplate;
	
	private static final Logger logger = LoggerFactory.getLogger(OrderDataManager.class);
	
}
